developing with the spring framework - chris … with the spring framework ... spring security...

67
Developing with the Spring framework framework Chris Richardson Author of POJOs in Action Chris Richardson Consulting, Inc http://www.chrisrichardson.net http://www.chrisrichardson.net Slide 1 Copyright (c) 2008 Chris Richardson. All rights reserved.

Upload: trinhanh

Post on 01-Apr-2018

245 views

Category:

Documents


6 download

TRANSCRIPT

Developing with the Spring frameworkframework

Chris RichardsonAuthor of POJOs in Action

Chris Richardson Consulting, Incg,

http://www.chrisrichardson.nethttp://www.chrisrichardson.net

Slide 1Copyright (c) 2008 Chris Richardson. All rights reserved.

Overall presentation goalp g

The power and simplicityof the Spring framework simplifies and accelerates simplifies and accelerates application development

Slide 2Copyright (c) 2008 Chris Richardson. All rights reserved.

About Chris

Grew up in EnglandLive in Oakland CALive in Oakland, CAOver twenty years of software development experience

Building object-oriented software i 1986since 1986

Using Java since 1996Using J2EE since 1999

Author of POJOs in ActionSpeaker at JavaOne, JavaPolis, NFJS, JUGs, ….Chair of the eBIG Java SIG in Oakland (www ebig org)Oakland (www.ebig.org)Run a consulting and training company that helps organizations build better software faster

Slide 3Copyright (c) 2008 Chris Richardson. All rights reserved.

Agendag

Tangled code, tight coupling and duplicationUsing dependency injectionDependency injection with less XMLManaging transactions with Springg g p gSimplifying code with Spring AOPSpring JDBC and other abstractionsSpring JDBC and other abstractions

Slide 4Copyright (c) 2008 Chris Richardson. All rights reserved.

Code that you hate to changey g

Business logic and infrastructure logic t l d t thare tangled together

Implementation of features is scattered and d plicated th o gho t the and duplicated throughout the applicationComponents are Components are tightly coupled to one another and the one another and the infrastructureComponents use low-

Slide 5

plevel APIs

Copyright (c) 2008 Chris Richardson. All rights reserved.

Example banking applicationp g pp

Slide 6Copyright (c) 2008 Chris Richardson. All rights reserved.

A nice architecture …

Layers

void create(Account)BankingTransaction transfer(fromAccountId, toAccountId, amount)

AccountServiceDelegate

void create(Account)BankingTransaction transfer(fromAccountId, toAccountId, amount)

AccountService

Account

Business Tier

verifyCallerAuthorized() {static}

BankingSecurityManager

audit()

<<singleton>>AuditingManager

Account findAccount(accountId)

JdbcAccountDao

txnId

BankingTransaction

addTransaction()

JdbcBankingTransactionDao

accountIdbalanceoverdraftType

begin()commit()

llb k()

<<singleton>>TransactionManager

Domain model

txnIddate

rollback()

getConnection()

<<singleton>>JdbcConnection

Manager

cleanUp()

Slide 7Copyright (c) 2008 Chris Richardson. All rights reserved.

… but shame about the code

Slide 8Copyright (c) 2008 Chris Richardson. All rights reserved.

Procedural code

Anemic Domain ModelA tS i

public class AccountServicelImpl implements AccountService {

AccountService = Business logicAccount and BankingTransaction =

p {

public BankingTransaction transfer(String fromAccountId, String toAccountId,double amount) {

…Account fromAccount = g

dumb data objectsCode is more difficult to:

Account fromAccount = accountDao.findAccount(fromAccountId);

Account toAccount = accountDao.findAccount(toAccountId);

double newBalance = fromAccount.getBalance() –tDevelop

UnderstandMaintainTest

amount;

fromAccount.setBalance(newBalance);toAccount.setBalance(toAccount.getBalance() +

amount);….

TestSolution: That's a whole other talk.

Slide 9Copyright (c) 2008 Chris Richardson. All rights reserved.

Tangled codeg

Every service method t i

public class AccountServiceImpl implements AccountService {

public BankingTransaction transfer(String fromAccountId, String toAccountId, double amount) {

contains:Business logicInfrastructure logic

BankingSecurityManager.verifyCallerAuthorized(AccountService.class,"transfer");

logger.debug("Entering AccountServiceImpl.transfer()");

TransactionManager.getInstance().begin();

AuditingManager.getInstance().audit(AccountService.class, "transfer",new Object[] { fromAccountId, toAccountId, amount });

try {Account fromAccount = accountDao.findAccount(fromAccountId);Account toAccount = accountDao.findAccount(toAccountId);double newBalance = fromAccount.getBalance() - amount;switch (fromAccount.getOverdraftPolicy()) {case Account NEVER:

Infrastructure

gViolates Separation of Concerns (SOC):

Increased complexity

case Account.NEVER:if (newBalance < 0)throw new MoneyTransferException("Insufficient funds");

break;case Account.ALLOWED:Calendar then = Calendar.getInstance();then.setTime(fromAccount.getDateOpened());Calendar now = Calendar.getInstance();

double yearsOpened = now.get(Calendar.YEAR) - then.get(Calendar.YEAR);int monthsOpened = now.get(Calendar.MONTH) - then.get(Calendar.MONTH);if (monthsOpened < 0) {yearsOpened--;monthsOpened += 12;

}yearsOpened = yearsOpened + (monthsOpened / 12.0);if ( ea sOpened < f omAcco nt getReq i edYea sOpen()

Business Logic

Increased complexityTesting is more difficultMore difficult to develop

if (yearsOpened < fromAccount.getRequiredYearsOpen()|| newBalance < fromAccount.getLimit())

throw new MoneyTransferException("Limit exceeded");break;

default:throw new MoneyTransferException("Unknown overdraft type: "

+ fromAccount.getOverdraftPolicy());

}fromAccount.setBalance(newBalance);toAccount.setBalance(toAccount.getBalance() + amount);

accountDao.saveAccount(fromAccount);accountDao.saveAccount(toAccount);

TransferTransaction txn = new TransferTransaction(fromAccount, toAccount,

Naming clash: transaction

amount, new Date());bankingTransactionDao.addTransaction(txn);

TransactionManager.getInstance().commit();

logger.debug("Leaving AccountServiceImpl.transfer()");return txn;

} catch (RuntimeException e) {logger.debug(

"Exception thrown in AccountServiceImpl.transfer()",e);

throw e;} catch (MoneyTransferException e) {logger.debug(

"Exception thrown in AccountServiceImpl.transfer()",e);

Infrastructure

Slide 10

);TransactionManager.getInstance().commit();throw e;

} finally {TransactionManager.getInstance().rollbackIfNecessary();

}} }

Copyright (c) 2008 Chris Richardson. All rights reserved.

Duplicated codep

public class AccountServiceImpl implements AccountService {

i t L l L F t tL ( tCl ()) public void create(Account account) {private Log logger = LogFactory.getLog(getClass());

public BankingTransaction transfer(String fromAccountId, String toAccountId, double amount) {BankingSecurityManager.verifyCallerAuthorized(AccountService.class, "transfer");

logger.debug("Entering AccountServiceImpl.transfer()");

T ti M tI t () b i ()

public void create(Account account) {BankingSecurityManager.verifyCallerAuthorized(AccountService.class,

"create");

logger.debug("Entering AccountServiceProceduralImpl.create()");

TransactionManager.getInstance().begin();

TransactionManager.getInstance().begin();

AuditingManager.getInstance().audit(AccountService.class, "transfer", new Object[] { fromAccountId, toAccountId, amount });

try {…

TransactionManager.getInstance().commit();

AuditingManager.getInstance().audit(AccountService.class, "create",new Object[] { account.getAccountId() });

try {…logger.debug("Leaving AccountServiceProceduralImpl.create()");

} catch (RuntimeException e) {

logger.debug("Leaving AccountServiceImpl.transfer()");

return txn;

} catch (RuntimeException e) {logger.debug("Exception thrown in AccountServiceImpl.transfer()", e);th

} catch (RuntimeException e) {logger.debug("Exception thrown in

AccountServiceProceduralImpl.create()",e);

throw e;} finally {TransactionManager.getInstance().rollbackIfNecessary();

}throw e;} catch (MoneyTransferException e) {logger.debug("Exception thrown in AccountServiceImpl.transfer()", e);TransactionManager.getInstance().commit();throw e;

} finally {TransactionManager.getInstance().rollbackIfNecessary();

}Violates Don’t Repeat Yourself (DRY)

}

}

Slide 11

}}

Copyright (c) 2008 Chris Richardson. All rights reserved.

Tightly coupled codeg y p

Service instantiates public class AccountServiceImpl implements AccountService {

DAOsReferences to:

implements AccountService {

public AccountServiceImpl() {this.accountDao = new JdbcAccountDao();this.bankingTransactionDao =

new JdbcBankingTransactionDao();References to:Singletons classesStatic methods

}

public BankingTransaction transfer(String fromAccountId, String toAccountId,double amount) {Stat c et ods

Consequences:Difficult to unit test

BankingSecurityManager.verifyCallerAuthorized(AccountService.class,

"transfer");

TransactionManager.getInstance().begin();Difficult to unit testDifficult to develop

…}

Slide 12Copyright (c) 2008 Chris Richardson. All rights reserved.

Low-level, error-prone code, p

Repeated boilerplate: public class JdbcAccountDao implements AccountDao {

bli A t fi dA t(St i tId) {Opening connectionsPreparing statementsTry/catch/finally for

public Account findAccount(String accountId) {

Connection con = JdbcConnectionManager.getInstance().getConnection();

PreparedStatement ps = null;Try/catch/finally for closing connections, etc

Lots of code to write

ResultSet rs = null;try {

ps = con.prepareStatement(…);…return account;

} catch (SQLException e) {Lots of code to write and debugChange a class ⇒

} catch (SQLException e) {throw new RuntimeException(e);

} finally {JdbcConnectionManager.getInstance()

.cleanUp(con, ps, rs);}

Change multiple SQL statements

}

Violates Don’t Repeat Yourself (DRY)

Slide 13

Violates Don t Repeat Yourself (DRY)

Copyright (c) 2008 Chris Richardson. All rights reserved.

So what? It works!Code is difficult to change ⇒ can’t keep up g p pwith the needs of the businessBad code/obsolete f k diffi lt frameworks ⇒ difficult to hire/retain good people It’s a downwards spiralIt s a downwards spiral

Bug fixes and enhancements aren’t done correctlyDesign continues to degrade

Slide 14Copyright (c) 2008 Chris Richardson. All rights reserved.

Improving the codep g

Dependency injectionD l t f th Decouples components from one another and from the infrastructure code

Aspect-oriented programmingp p g gEliminates infrastructure code from services (including transaction management)Implements it one placeImplements it one placeEnsures DRY SOCs

Higher-level APIsHigher level APIsEncapsulate low-level detailsNo more JDBC

Slide 15Copyright (c) 2008 Chris Richardson. All rights reserved.

The Spring frameworkp g

Simplicity and power S t th POJO i d lSupports the POJO programming modelDependency injectionAOP for handling crosscutting concernsAOP for handling crosscutting concernsSimplified APIs for many 3rd party frameworks (Hibernate, JDBC, Quartz, JMX,

)..)Web frameworks: MVC, WebFlow

Rapid evolutionpSpring 2.0 – October 2006Spring 2.5 – December 2007C l t b k d tibilitComplete backward compatibility

Slide 16Copyright (c) 2008 Chris Richardson. All rights reserved.

The Spring framework ecosystemp g y

Framework Description

Spring framework The foundation

Spring Security (a.k.a. Acegi) Extensible framework providingauthentication, authorization and instance-level security

Spring Web Flow An excellent web framework for building multi-page flows

Spring Web Services Contract-first, document–centricSOAP and XML web services

Spring Dynamic Modules for OSGI

Deploy Spring applications on OSGIOSGI OSGI

Spring Batch Powerful batch processing framework

….

Slide 17Copyright (c) 2008 Chris Richardson. All rights reserved.

Agendag

Tangled code, tight coupling and duplicationUsing dependency injectionDependency injection with less XMLManaging transactions with Springg g p gSimplifying code with Spring AOPSpring JDBC and other abstractionsSpring JDBC and other abstractions

Slide 18Copyright (c) 2008 Chris Richardson. All rights reserved.

Dependency injectionp y j

AccountService Application components BankingTransaction transfer(fromId, toId, amount)

Application components depend on:

One another Infrastructure components

Old way: components

findAccount(id)

<<interface>>Account

DaocreateTransaction(…)

<<interface>>BankingTransaction

Dao

<<singleton>>Transaction

Manager

y pobtain dependencies:

Instantiation using newStatics – singletons or static methodsService Locator such as JNDI

findAccount(id)

JdbcAccountDao

createTransaction(…)

JdbcBankingTransaction

Dao

Service Locator such as JNDIBut these options result in:

CouplingIncreased complexity

New way: Pass

<<singleton>>Jdbc

ConnectionManager

New way: Pass dependencies to component:

Setter injectionConstructor injectionConstructor injection

19Copyright (c) 2008 Chris Richardson. All rights reserved.

Using dependency injectiong p y jpublic AccountServiceImpl() {

this.accountDao = new JdbcAccountDao();…

}

OLD WAY:}

public BankingTransaction transfer(…) {

BankingSecurityManager.verifyCallerAuthorized(…);TransactionManager.getInstance().begin();

Static referencesDirect instantiation

g g () g ();…

}public AccountServiceImpl(AccountDao accountDao,

BankingTransactionDao bankingTransactionDao,TransactionManager transactionManager,BankingSecurityManagerWrapper bankingSecurityWrapper) {

thi tDAO tDthis.accountDAO = accountDao;this.transactionManager = transactionManager…

}

public BankingTransaction transfer(…) {

NEW WAY:

Constructor injectionbankingSecurityWrapper.verifyCallerAuthorized(…);transactionManager.getInstance().begin();

…}

Constructor injection

Slide 20Copyright (c) 2008 Chris Richardson. All rights reserved.

Who instantiates the objects?j

Clients that i t ti t i

public class AccountServiceDelegate implements AccountService {

public AccountServiceDelegate() {this service = new instantiate service

need to pass in dependenciesB t th ld

this.service new AccountServiceImpl(

new JdbcAccountDao(),new JdbcBankingTransactionDao(),

}

But they could use dependency injection too ⇒ ripples up through the code

public class AccountServiceDelegate implements AccountService {public AccountServiceDelegate(AccountService service) {

this.service = service;}through the code

We could use a hand-written factory but that’s where Spring

public class SpringAccountServiceTests extends AbstractSpringTest {

protected void onSetUp() throws Exception {super onSetUp();

!

}

that s where Spring comes into play

super.onSetUp();service = new AccountServiceDelegate(

new AccountServiceImpl(new JdbcAccountDao(),new JdbcBankingTransactionDao(), TransactionManager.getInstance(),

di i ()

!Slide 21

AuditingManager.getInstance(),BankingSecurityManagerWrapper.getInstance()));

}

Copyright (c) 2008 Chris Richardson. All rights reserved.

Spring lightweight containerp g g g

Lightweight container = sophisticated factory for creating objectsSpring bean = object created and managed by SpringYou write XML that specifies how to:

Create objects Initialize them using dependency injection

22Copyright (c) 2008 Chris Richardson. All rights reserved.

Spring code example

<beans>

p g p

public class AccountServiceImpl public class AccountServiceImpl …

public AccountServiceImpl(AccountDao

accountDao, …){

thi tD

<bean id="accountService"class="AccountServiceImpl">

<constructor-arg ref=“accountDao"/>…

</bean>this.accountDao =

accountDao;…

}

<bean id="accountDao"class="JdbcAccountDao">

…</bean> public class JdbcAccountDao

l {/

implements AccountDao {…}

</beans>

23Copyright (c) 2008 Chris Richardson. All rights reserved.

Using Spring dependency injectiong p g p y j

<beans>

<bean id="AccountServiceDelegate"<bean id= AccountServiceDelegateclass="net.chris...client.AccountServiceDelegate"><constructor-arg ref="AccountService"/>

</bean>

<bean id="AccountService"class="net.chris...domain.AccountServiceImpl"><constructor-arg ref="accountDao"/><constructor arg ref="bankingTransactionDao"/><constructor-arg ref= bankingTransactionDao /><constructor-arg ref="transactionManager"/><constructor-arg ref="auditingManager"/><constructor-arg ref="bankingSecurityManagerWrapper"/>

</bean>

<b id " tD "

ApplicationContext ctx = new ClassPathXmlApplicationContext(

"appCtx/banking-service.xml");

service = (AccountService) ctx.getBean("AccountServiceDelegate");<bean id="accountDao"

class="net.chris...domain.jdbc.JdbcAccountDao"/>

<bean id="bankingTransactionDao"class="net.chris...domain.jdbc.JdbcBankingTransactionDao"/>

<bean id="transactionManager" factory-method="getInstance"class="net.chris...infrastructure.TransactionManager"/>

.getBean( AccountServiceDelegate );

<bean id="auditingManager" factory-method="getInstance"class="net.chris...infrastructure.AuditingManager"/>

<bean id="bankingSecurityManagerWrapper"class="net.chris...infrastructure.BankingSecurityManagerWrapper"/>

</beans>

Slide 24Copyright (c) 2008 Chris Richardson. All rights reserved.

Eliminating Java singletonsg g

Spring beans are public class TransactionManager {p gsingletons (by default)

public TransactionManager() {}

public void begin() {…})Spring can instantiate classes

<beans>

….<bean id="transactionManager"

such as the TransactionManager

<bean id= transactionManager factory-method=“getInstance”

class="net.chrisrichardson.bankingExample.infrastructure.TransactionManager"/>

<bean id="auditingManager" g gfactory-method=“getInstance”

class="net.chrisrichardson.bankingExample.infrastructure.AuditingManager"/>

Slide 25

</beans>

Copyright (c) 2008 Chris Richardson. All rights reserved.

Revised designg

void create(Account)BankingTransaction transfer(fromAccountId, toAccountId, amount)

AccountServiceDelegate

void create(Account)BankingTransaction transfer(fromAccountId, toAccountId, amount)

AccountService

<<interface>> <<interface>>

Business Tier

verifyCallerAuthorized()

BankingSecurityManagerWrapper

AuditingManager

Account findAccount(accountId)

<<interface>>AccountDao

BankingTransaction

addTransaction()

<<interface>>Banking

TransactionDaoaccountIdbalanceoverdraftType

Account

begin()

TransactionManager

audit()

Account findAccount(accountId)

JdbcAccountDao

dd ()

JdbcBankingTransactionDao

txnIddate

g ()commit()rollback()

JdbcConnectionM

Account findAccount(accountId)addTransaction()

getConnection()cleanUp()

Manager

Slide 26Copyright (c) 2008 Chris Richardson. All rights reserved.

Fast unit testing exampleg p

public class AccountServiceImplMockTests extends MockObjectTestCase {

private AccountDao accountDao;private AccountDao accountDao;private BankingTransactionDao bankingTransactionDao;private TransactionManager transactionManager;

protected void setUp() throws Exception {accountDao = mock(AccountDao.class);bankingTransactionDao = mock(BankingTransactionDao.class);transactionManager = mock(TransactionManager class);

Create mock dependencies and

inject themtransactionManager = mock(TransactionManager.class);

…service = new AccountServiceImpl(accountDao, bankingTransactionDao, transactionManager, auditingManager,

bankingSecurityWrapper);

}

public void testTransfer_normal() throws MoneyTransferException {h ki ( E t ti () {{checking(new Expectations() {{one(accountDao).findAccount("fromAccountId"); will(returnValue(fromAccount));one(accountDao).findAccount("toAccountId"); will(returnValue(toAccount));one(transactionManager).begin();…

}});

f l ( f ) f ("f d" " d" )TransferTransaction result = (TransferTransaction) service.transfer("fromAccountId", "toAccountId", 15.0);

assertEquals(15.0, fromAccount.getBalance());assertEquals(85.0, toAccount.getBalance());…

verify();

}

Slide 27Copyright (c) 2008 Chris Richardson. All rights reserved.

Configuring Spring beans in an applicationpp

Web applicationApplicationContext created

<web>

<context-param>< > t tC fi L ti </ >ApplicationContext created

on startupWeb components can call AppCtx.getBean()Some frameworks can

t ti ll i j t S i

<param-name>contextConfigLocation</param-name><param-value>appCtx/banking-service.xml</param-value>

</context-param>…

</web>

ApplicationCtx ctx = automatically inject Spring beans into web components

TestingTests instantiate application context

ApplicationCtx ctx = WebApplicationContextUtils.

getWebApplicationContext(ServletContext)

AccountService service = (AccountService) ctx.getBean("AccountServiceDelegate");

contextCall getBean()Better: Use AbstractDepdendencyInjectionSpringContextTests for d d

public class SpringAccountServiceTests extendsAbstractDependencyInjectionSpringContextTests {

private AccountService service;…

dependency injection into tests @Override

protected String[] getConfigLocations() {return new String[] { "appCtx/banking-service.xml" };

}

public void setAccountServiceDelegate(AccountService service) {this.service = service;

Slide 28

;}

…}

Copyright (c) 2008 Chris Richardson. All rights reserved.

Demo

Let’s walk through the revised code

Slide 29Copyright (c) 2008 Chris Richardson. All rights reserved.

Benefits of dependency injectionp y j

Simplifies codePromotes loose couplingMakes testing easierg

Slide 30Copyright (c) 2008 Chris Richardson. All rights reserved.

Agendag

Tangled code, tight coupling and duplicationUsing dependency injectionDependency injection with less XMLManaging transactions with SpringSimplifying code with Spring AOPSimplifying code with Spring AOPSpring JDBC and other abstractions

Slide 31Copyright (c) 2008 Chris Richardson. All rights reserved.

Dependency injection with less XMLp y j

Spring 2.5Annotation-based configurationClass path component scanning

Spring JavaConfigJava-based configuration of Spring beans

Arid DAOMinimal XMLAutomatically generated finders

Slide 32Copyright (c) 2008 Chris Richardson. All rights reserved.

Annotation-based configurationgpublic class MoneyTransferServiceImpl implements MoneyTransferService {

private final AccountRepository accountRepository;p p y p y

private final BankingTransactionRepository bankingTransactionRepository;

@Autowiredpublic MoneyTransferServiceImpl(AccountRepository accountRepository,

k

<beans>

BankingTransactionRepository bankingTransactionRepository) {

this.accountRepository = accountRepository;this.bankingTransactionRepository = bankingTransactionRepository;

}

<context:annotation-config/>

<bean name="moneyTransferService"class="MoneyTransferServiceImpl"

/>

public class HibernateAccountRepositoryimplements AccountRepository {

private HibernateTemplate hibernateTemplate;

/>

<bean name="accountRepository"class="HibernateAccountRepository"

/>private HibernateTemplate hibernateTemplate;

@Autowiredpublic HibernateAccountRepository(HibernateTemplate template) {

hibernateTemplate = template;}

/>

</beans>

}

Slide 33Copyright (c) 2008 Chris Richardson. All rights reserved.

Class path component scanning

@Componentpublic class MoneyTransferServiceImpl

l f {implements MoneyTransferService {…}

@Componentpublic class HibernateAccountRepository

implements AccountRepository {…}}

<beans>

<context:component-scan base-package="net.chrisrichardson.bankingExample"/>

</beans>

Very little XML!Very little XML!Slide 34Copyright (c) 2008 Chris Richardson. All rights reserved.

Using Spring JavaConfigg p g g@Configurationpublic abstract class AppConfig {

@Bean@Beanpublic MoneyTransferService moneyTransferService() {

return new MoneyTransferServiceImpl(accountRepository(),bankingTransactionRepository());

}<bean>

@Beanpublic AccountRepository accountRepository() {

HibernateAccountRepository repo = new HibernateAccountRepository();

repo.setSessionFactory(sessionFactory());

<bean class="net.chrisr…AppConfig"/>

<bean class="org.spring…ConfigurationPostProcessor" p y( y());

return repo;}

@Beanpublic BankingTransactionRepository bankingTransactionRepository() {

/>

</beans>

HibernateBankingTransactionRepository repo = new HibernateBankingTransactionRepository();repo.setSessionFactory(sessionFactory());return repo;

}

@E te nalBean@ExternalBeanpublic abstract SessionFactory sessionFactory();

}Slide 35Copyright (c) 2008 Chris Richardson. All rights reserved.

Even simpler DAOs with Aridp<beans>

id d fi b<arid:define-beanspackage='org.jia.ptrack.domain'pattern='net.chrisrichardson.arid.domain.GenericDao+'

…/ id d fi b</arid:define-beans>

…</beans>

public interface AuditEntryRepository extends GenericDao<AuditEntry, Integer> {

List<AuditEntry> findByUserNameAndDateBetween(String username List<AuditEntry> findByUserNameAndDateBetween(String username, Date fromDate, Date toDate)

}

Slide 36Copyright (c) 2008 Chris Richardson. All rights reserved.

Agendag

Tangled code, tight coupling and duplicationUsing dependency injectionDependency injection with less XMLManaging transactions with g gSpringSimplifying code with Spring AOPSimplifying code with Spring AOPSpring JDBC and other abstractions

Slide 37Copyright (c) 2008 Chris Richardson. All rights reserved.

Transaction management woesg

Every service public class AccountServiceImpl …ymethod manages transactionsOO does not enable

public BankingTransaction transfer(String fromAccountId, String toAccountId, double amount) {

…transactionManager begin();OO does not enable

us to write this code in one placeIt’ tti

transactionManager.begin();…try {

transactionManager.commit();It’s a crosscutting concern

a a o a ag o ();…

} catch (MoneyTransferException e) {…transactionManager.commit();throw e;

⇒ tangled and duplicated code

} finally {transactionManager.rollbackIfNecessary();

}}

Slide 38Copyright (c) 2008 Chris Richardson. All rights reserved.

Declarative transaction managementg

EJB has container-managed transactions

Configure transactions using XML t d tmetadata

Avoids explicitly managing transactionsOne of the good features of EJBOne of the good features of EJB

Spring provides the POJO equivalentW it t d t t k POJO Write metadata to make POJOs transactionalMore flexible than EJB CMTMore flexible than EJB CMT

39Copyright (c) 2008 Chris Richardson. All rights reserved.

Aspect-Oriented Programming (AOP)p g g ( )

AOP enables the modular implementation of crosscutting concernsi.e. eliminates duplicate code

AspectModule that implements a crosscutting concernModule that implements a crosscutting concernCollection of pointcuts and advice

Join pointSomething that happens during program execution e.g. execution of public service method

PointcutSpecification of a set of join pointsE g All public service methodsE.g. All public service methods

AdviceCode to execute at the join points specified by a pointcutE.g. manage transactions, perform authorization checkg g , p

Slide 40Copyright (c) 2008 Chris Richardson. All rights reserved.

Spring AOPp g

Spring AOP = simple, effective AOP implementationLightweight container can wrap objects with proxiesProxy masquerades as original object Proxy executes extra advice:

B f i ki i i l th dBefore invoking original methodAfter invoking original methodInstead of original method

Slide 41Copyright (c) 2008 Chris Richardson. All rights reserved.

Spring transaction managementp g g

Slide 42Copyright (c) 2008 Chris Richardson. All rights reserved.

PlatformTransactionManager Hierarchyg y

<<interface>>Pl tf

invoke()

TransactionInterceptor

getTransaction()commit()

PlatformTransaction

ManagerUses

commit()rollback()

DataSourceTransaction

Manager

HibernateTransaction

Manager

JtaTransactionManager ...

Manages local transactions

Manages global/JTA transactions

Slide 43Copyright (c) 2008 Chris Richardson. All rights reserved.

DataSourceTransactionManagerg

Manages JDBC connectionsOpens and closes JDBC connectionsStores connection in a ThreadLocal

Manages transactionsConnection.setAutoCommit(false)Connection.commit()Connection.rollback()

Slide 44Copyright (c) 2008 Chris Richardson. All rights reserved.

Using with JDBCg

public class JdbcConnectionManager {p g {….public Connection getConnection() {

logger.debug("getting connection");return DataSourceUtils.getConnection(dataSource);

}

private void closeConnection(Connection con) {if (con != null) {

logger.debug("releasing connection");DataSourceUtils.releaseConnection(con, dataSource);

}}}

•Delete homegrown T i M O b TransactionManager. Or better yet: use Spring JDBC

Slide 45Copyright (c) 2008 Chris Richardson. All rights reserved.

Spring bean definitionsp g<aop:config>

<aop:pointcut id="serviceCall"i " i ( bli * h i i h d *S i *( ))" /expression="execution(public * net.chrisrichardson..*Service.*(..))" />

<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceCall"/></aop:config>

<tx:advice id="txAdvice” transaction-manager=“transactionManager”><tx:attributes>

<tx:method name="*"no-rollback-for="net.chris…domain.MoneyTransferException" />

</tx:attributes></tx:advice>

<bean id="transactionManager" class="org.springf…jdbc.datasource.DataSourceTransactionManager">

<property name="dataSource" ref="dataSource" /></bean>

Slide 46Copyright (c) 2008 Chris Richardson. All rights reserved.

Agendag

Tangled code, tight coupling and duplicationUsing dependency injectionDependency injection with less XMLManaging transactions with Springg g p gSimplifying code with Spring AOPSpring JDBC and other abstractionsSpring JDBC and other abstractions

Slide 47Copyright (c) 2008 Chris Richardson. All rights reserved.

Application crosscutting concernspp g

Every service public class AccountServiceImpl implements AccountService {

public BankingTransaction transfer(String fromAccountId, String toAccountId, double amount) {Every service

method:Logs entries and

BankingSecurityManager.verifyCallerAuthorized(AccountService.class,"transfer");

logger.debug("Entering AccountServiceImpl.transfer()");

TransactionManager.getInstance().begin();

AuditingManager.getInstance().audit(AccountService.class, "transfer",new Object[] { fromAccountId, toAccountId, amount });

try {Account fromAccount = accountDao.findAccount(fromAccountId);Account toAccount = accountDao.findAccount(toAccountId);double newBalance = fromAccount.getBalance() - amount;switch (fromAccount.getOverdraftPolicy()) {case Account.NEVER:

Infrastructure

exitsPerforms security checks

if (newBalance < 0)throw new MoneyTransferException("Insufficient funds");

break;case Account.ALLOWED:Calendar then = Calendar.getInstance();then.setTime(fromAccount.getDateOpened());Calendar now = Calendar.getInstance();

double yearsOpened = now.get(Calendar.YEAR) - then.get(Calendar.YEAR);int monthsOpened = now.get(Calendar.MONTH) - then.get(Calendar.MONTH);if (monthsOpened < 0) {yearsOpened--;monthsOpened += 12;

}yearsOpened = yearsOpened + (monthsOpened / 12.0);if (yearsOpened < fromAccount getRequiredYearsOpen()

Business Logic

checksAudit logs…

if (yearsOpened < fromAccount.getRequiredYearsOpen()|| newBalance < fromAccount.getLimit())

throw new MoneyTransferException("Limit exceeded");break;

default:throw new MoneyTransferException("Unknown overdraft type: "

+ fromAccount.getOverdraftPolicy());

}fromAccount.setBalance(newBalance);toAccount.setBalance(toAccount.getBalance() + amount);

accountDao.saveAccount(fromAccount);accountDao.saveAccount(toAccount);

TransferTransaction txn = new TransferTransaction(fromAccount, toAccount,t D t ())

Tangled and duplicated code

amount, new Date());bankingTransactionDao.addTransaction(txn);

TransactionManager.getInstance().commit();

logger.debug("Leaving AccountServiceImpl.transfer()");return txn;

} catch (RuntimeException e) {logger.debug(

"Exception thrown in AccountServiceImpl.transfer()",e);

throw e;} catch (MoneyTransferException e) {logger.debug(

"Exception thrown in AccountServiceImpl.transfer()",e);

Infrastructure

TransactionManager.getInstance().commit();throw e;

} finally {TransactionManager.getInstance().rollbackIfNecessary();

}} }

Slide 48Copyright (c) 2008 Chris Richardson. All rights reserved.

Logging Aspectgg g p@Aspectpublic class LoggingAspect implements Ordered {

@Pointcut("execution(public * public class AccountServiceImpl …

( (pnet.chrisrichardson..*Service.*(..))")

private void serviceCall() {}

@Around("serviceCall()")public Object doLogging(ProceedingJoinPoint jp) throws

Throwable {

private Log logger = LogFactory.getLog(getClass());

public BankingTransaction transfer(String fromAccountId {

Log logger = LogFactory.getLog(jp.getTarget().getClass());

Signature signature = jp.getSignature();

String methodName = signature.getDeclaringTypeName() + ".“ + signature.getName();

String fromAccountId, String toAccountId, double amount) {

…logger.debug("Entering

AccountServiceImpl.transfer()");… g g ();

logger.debug("entering: " + methodName);

try {Object result = jp.proceed();

logger.debug("Leaving: " + methodName);

try {…logger.debug("Leaving

AccountServiceImpl.transfer()");} catch (RuntimeException e) { gg g( g );

return result;} catch (Exception e) {

logger.debug("Exception thrown in " + methodName, e);throw e;

logger.debug("Exception thrown in

AccountServiceImpl.transfer()",e);

throw e;}

Slide 49

}}

}

Copyright (c) 2008 Chris Richardson. All rights reserved.

Spring configurationp g g

<beans>

<aop:aspectj-autoproxy />

<bean id="loggingAspect"class="net.chrisrichardson.bankingExample.infrastructure.aspects.LoggingAspect">

<constructor-arg ref="transactionManager" /><constructor-arg ref= transactionManager /></bean>

</beans>

Slide 50Copyright (c) 2008 Chris Richardson. All rights reserved.

In picturesp

AccountServiceAuditingAspect

LoggingAspect

TransactionManagement

A tSecurity AspectTransaction

Retry Aspect

JDK/CGLib proxy

ppAspecty p

Slide 51Copyright (c) 2008 Chris Richardson. All rights reserved.

Simpler AccountServicep

public class AccountServiceImpl implementsAccountService {

public AccountServiceImpl(AccountDao accountDao,BankingTransactionDao bankingTransactionDao) {

this.accountDao = accountDao;this bankingTransactionDao bankingTransactionDao;

Fewer dependencies

this.bankingTransactionDao = bankingTransactionDao;}

public BankingTransaction transfer(String fromAccountId, String toAccountId,double amount) throws MoneyTransferException {

Account fromAccount = accountDao.findAccount(fromAccountId);Account toAccount = accountDao.findAccount(toAccountId);assert amount > 0;double newBalance = fromAccount.getBalance() - amount;switch (fromAccount.getOverdraftPolicy()) {

Simpler code( g y()) {

case Account.NEVER:if (newBalance < 0)

….} It's a POJO

Slide 52

Copyright (c) 2008 Chris Richardson. All rights reserved.

Simpler mock object testp j

public class AccountServiceImplMockTests extends MockObjectTestCase {

public void testTransfer_normal() throws MoneyTransferException {checking(new Expectations() {

{one(accountDao) findAccount("fromAccountId");one(accountDao).findAccount( fromAccountId );will(returnValue(fromAccount));one(accountDao).findAccount("toAccountId");will(returnValue(toAccount));one(accountDao).saveAccount(fromAccount);one(accountDao).saveAccount(toAccount);

Fewer dependencies to mock

o (a ou ao) a ou ( o ou );one(bankingTransactionDao).addTransaction(

with(instanceOf(TransferTransaction.class)));}

});

TransferTransaction result = (TransferTransaction) service.transfer("fromAccountId", "toAccountId", 15.0);

…}

Slide 53Copyright (c) 2008 Chris Richardson. All rights reserved.

Spring IDE for Eclipsep g p

Slide 54Copyright (c) 2008 Chris Richardson. All rights reserved.

Demo

Step through the codep g

Slide 55Copyright (c) 2008 Chris Richardson. All rights reserved.

Benefits and drawbacks of Spring AOP

BenefitsSimple yet very effective AOP Simple yet very effective AOP implementationSimplifies codeWorks extremely well at the service layerWorks extremely well at the service-layer

DrawbacksObjects must be created by Springj y p gCan only intercept calls from outsideOnly efficient when methods/advice are expensiveexpensive

⇒ AOP in the domain model requires AspectJAspectJ

Slide 56Copyright (c) 2008 Chris Richardson. All rights reserved.

Agendag

Tangled code, tight coupling and duplicationUsing dependency injectionDependency injection with less XMLManaging transactions with Springg g p gSimplifying code with Spring AOPSpring JDBC and other Spring JDBC and other abstractions

Slide 57Copyright (c) 2008 Chris Richardson. All rights reserved.

Portable service abstractions

Many Java EE APIs are very low-levelN d t it l t f titi dNeed to write lots of repetitive codeOften error-proneException handling can be painful - e.g. Exception handling can be painful e.g. checked exceptions

Spring provides higher level APIs l l l lEncapsulate low-level APIs

Eliminate boilerplate codeSimplified exception handlingSimplified exception handling

⇒ Simpler application code⇒ Simpler application code

Slide 58Copyright (c) 2008 Chris Richardson. All rights reserved.

The trouble with the JDBC API

Low-level, error- public Account findAccount(String accountId) {Connection con = ,

prone codeLots of repeated,

connectionManager.getConnection();PreparedStatement ps = null;ResultSet rs = null;try {

ps = con.prepareStatement("SELECT * FROM BANK ACCOUNT WHERE accountId ?");

boilerplate codeBANK_ACCOUNT WHERE accountId = ?");

ps.setString(1, accountId);rs = ps.executeQuery();Account account =

new Account(rs.getInt("ACCOUNT_ID"), rs getString("accountId") rs.getString( accountId ), …

}return account;

} catch (SQLException e) {throw new RuntimeException(e);p ( );

} finally {connectionManager.cleanUp(con, ps, rs);

}

}Don't use JDBC

Slide 59Copyright (c) 2008 Chris Richardson. All rights reserved.

Spring JDBC simplificationsp g p

Task Spring You

Open the connection X

Supply the SQL statement Xpp y Q

Prepare the statement X

Iterate through ResultSet XIterate through ResultSet X

Map row to Java object X

Handle exceptions X

Clean up X

60Copyright (c) 2008 Chris Richardson. All rights reserved.

Spring JDBC Examples…p g p

public Account findAccount(String accountId) {

return (Account) jdbcTemplate.queryForObject("SELECT * FROM BANK_ACCOUNT WHERE account_Id = ?",new Object[] { accountId }, j [] { },new RowMapper() {

public Object mapRow(ResultSet rs,…) throws SQLException {return new Account(rs getInt("ACCOUNT ID")return new Account(rs.getInt( ACCOUNT_ID ),

rs.getString("accountId"), rs.getDouble("BALANCE"),

…););

}});

}}

Slide 61Copyright (c) 2008 Chris Richardson. All rights reserved.

Spring JDBC Examplesp g p

jdbcTemplateupdate(.update(

"INSERT INTO customer(id, first_name, last_name, balance) values(?,?, ?, ?)",

new Object[] { 1, "John", "Doe", "100" });new Object[] { 1, John , Doe , 100 });

assertEquals(1, jdbcTemplate.update("UPDATE CUSTOMER SET BALANCE = ? WHERE ID = ?", ,new Object[] {customer.getBalance(), customer.getId() }));

assertEquals(1, jdbcTemplate.update("DELETE FROM CUSTOMER WHERE ID = ?", new Object[] { 1

}));

Slide 62Copyright (c) 2008 Chris Richardson. All rights reserved.

Spring JDBC configurationp g g

<beans>

…<bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource"><property name="driverClassName" p p y

value="${jdbc.driver.class}" /><property name="url" value="${jdbc.url}" /><property name="username" value="${jdbc.user}" /><property name="password" value="${jdbc.password}" />p p y p ${j p } /

</bean>

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">g p g j p

<property name="dataSource" ref="dataSource"/></bean>

</beans>

Slide 63Copyright (c) 2008 Chris Richardson. All rights reserved.

Benefits of using Spring JDBCg p g

Eliminates the boilerplateLess codeLess error-pronepPortable error handling

Slide 64Copyright (c) 2008 Chris Richardson. All rights reserved.

Other API abstractions

ORM frameworks:HibernateHibernateJPAToplinkiBATISiBATIS

JavaMailJMXJMXRMIJMSQuartz…

Slide 65Copyright (c) 2008 Chris Richardson. All rights reserved.

Summaryy

Spring framework:

Improved SOCDRY codeSimpler codeSpring framework:

Dependency injectionAOPS i b i

Simpler codeImproved maintainability

Service abstractions Easier to develop and testLet’s you focus on ythe core problem

Slide 66Copyright (c) 2008 Chris Richardson. All rights reserved.

For more information

Buy my book ☺

Send email:[email protected]

Visit my website:

http://www.chrisrichardson.net

Talk to me about consulting and training

D l d l d Cl d Download example code, Cloud Tools, Project Track, ORMUnit, etc

http://code.google.com/p/aop-oodhttp://code.google.com/p/cloudtoolshttp //code google com/p/p ojectt ackhttp://code.google.com/p/projecttrackhttp://code.google.com/p/aridpojoshttp://code.google.com/p/ormunithttp://code.google.com/p/umangite

Slide 67Copyright (c) 2008 Chris Richardson. All rights reserved.