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.
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.
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.
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.