IImprovingiYYour CCoded withith
Dependency Injection and AspectO i t dP
Oriented
Programmingi

Chris Richardson
Author of POJOs in Actiong, IncChris Richardson Consulting,
http://www.chrisrichardson.net
Slide 1

Overall ppresentation g
goalShow howdependency injection, andaspect-oriented
programmingi
makek codedeasier to develop andmaintain
Slide 2

About Chris
Grew up in EnglandLive in Oakland,
Oakland CA
Over twenty years of softwaredevelopment experience

Building object-oriented software
since
i
1986
Using Java since 1996
Using J2EE since 1999

Author of POJOs in Action
Speaker at JavaOne, JavaPolis,
NFJS, JUGs, ….
Chair of the eBIG Java SIG in
Oakland (www.ebig.org)(www ebig org)
Run a consulting and training
company that helps organizations
build better software fasterAgendag
Tangled code, tight coupling andduplication
Using dependency injection
Dependency injection with less XML
Simplifying
p y g code with aspects
pUsing aspects in the domain model

Slide 4

Code that y
you hate to changegBusiness logic and infrastructure logic
are tangledt
l d togethertthImplementation of features is scattered
and dduplicatedplicated throughout
th o gho t theapplication
ComponentsCt aretightly coupled
to one another andthe infrastructure

Slide 5

Examplep bankingg applicationpp

A nice architecture …
Layers
AccountServiceDelegate
void create(Account)
BankingTransaction transfer(fromAccountId, toAccountId, amount)

AccountService

Business Tier

BankingSecurityManager

void create(Account)
BankingTransaction transfer(fromAccountId, toAccountId, amount)verifyCallerAuthorized() {static}<<singleton>>
AuditingManager
Account

Domain modelaccountIdbalanceoverdraftTypeaudit()

JdbcAccountDao
Account findAccount(accountId)

JdbcBanking
TransactionDao
addTransaction()

BankingTransaction<<singleton>>
TransactionManager
begin()
commit()rollback()
llb k()

txnId
date

<<singleton>>
JdbcConnection
Manager
getConnection()cleanUp()

… but shame about the code

Slide 8

Procedural code
Anemic Domain Model
AccountService
AtS
i =Business logic
Account andg=BankingTransaction
dumb data objects

Code is more difficult
to:Develop
Understand
MaintainTestpublic class AccountServicelImpl
implementsp
AccountService {public BankingTransaction transfer(String
fromAccountId, String toAccountId,
double amount) {…
Account fromAccount =accountDao.findAccount(fromAccountId);
Account toAccount =accountDao.findAccount(toAccountId);
double newBalance = fromAccount.getBalance() –amount;t
fromAccount.setBalance(newBalance);
toAccount.setBalance(toAccount.getBalance() +amount);….

Solution: That's awhole other talk.

Slide 9

Tangledg
code
Every service method
contains:t i
Business logicgInfrastructure logic

Violates Separation ofConcerns (SOC):

Increased complexity
Testing is more difficult
More difficult to develop

Naming clash:transaction

public class AccountServiceImpl implements AccountService {public BankingTransaction transfer(String fromAccountId, String toAccountId, double amount) {BankingSecurityManager.verifyCallerAuthorized(AccountService.class,"transfer");logger.debug("Entering AccountServiceImpl.transfer()");

Infrastructure

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:
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();Business Logic

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( ea sOpened < ffromAccount.getRequiredYearsOpen()omAcco nt getReq i edYea sOpen()|| 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,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);
TransactionManager.getInstance().commit();throw e;} finally {TransactionManager.getInstance().rollbackIfNecessary();}} }Infrastructure

Slide 10Duplicatedpcodepublic class AccountServiceImpl implements AccountService {public void create(Account account) {BankingSecurityManager.verifyCallerAuthorized(AccountService.class,"create");public BankingTransaction transfer(String fromAccountId, String toAccountId,
double amount) {privatei t L
Log loggerl= LogFactory.getLog(getClass());
L F ttL ( tCl())

BankingSecurityManager.verifyCallerAuthorized(AccountService.class, "transfer");logger.debug("Entering AccountServiceProceduralImpl.create()");
logger.debug("Entering AccountServiceImpl.transfer()");

TransactionManager.getInstance().begin();T
TransactionManager.getInstance().begin();
ti M
tI t
() b i ()AuditingManager.getInstance().audit(AccountService.class, "create",});
AuditingManager.getInstance().audit(AccountService.class, "transfer",new
newObject[]
Object[]{{account.getAccountId()
fromAccountId, toAccountId,
amount });

try {
…
…
logger.debug("Leaving AccountServiceProceduralImpl.create()");
TransactionManager.getInstance().commit();} catch (RuntimeException e) {logger.debug("Exception thrown inlogger.debug("Leaving AccountServiceImpl.transfer()");
AccountServiceProceduralImpl.create()",e);
return txn;
throw e;} finally {} catch (RuntimeException e) {logger.debug("Exception thrown in AccountServiceImpl.transfer()",TransactionManager.getInstance().rollbackIfNecessary();
e);
}
th
throw
e;
try {

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

Violates Don’t Repeat Yourself (DRY)}

Slide 11

Tightlyg y coupledpcodeService instantiates
DAOs
References to:Singletons classes
StaticStat
c methods
et odsConsequences:
Difficult to unit test
Difficult to develop

public class AccountServiceImpl
implements AccountService {public AccountServiceImpl() {this.accountDao = new JdbcAccountDao();this.bankingTransactionDao =new JdbcBankingTransactionDao();}public BankingTransaction transfer(String
fromAccountId, String toAccountId,
double amount) {BankingSecurityManager.verifyCallerAuthorized(AccountService.class,"transfer");
TransactionManager.getInstance().begin();…
}Slide 12

Low-level,, error-pronep
code
Repeated boilerplate:
Opening connections
Preparing statements
Try/catch/finally forclosing connections,etc

Lots of code to writeand debug
Change a class ⇒Change multiple SQL
statements

public class JdbcAccountDao implements AccountDao {publicbli AccountAt findAccount(Stringfi dAt(St iaccountId)tId) {
Connection con = JdbcConnectionManager.getInstance().getConnection();PreparedStatement ps = null;
ResultSet rs = null;try {ps = con.prepareStatement(…);…
return account;} catch (SQLException e) {throw new RuntimeException(e);} finally {JdbcConnectionManager.getInstance().cleanUp(con, ps, rs);}}

Violates Don
Don’tt Repeat Yourself (DRY)
Slide 13

So what? It works!
Code is difficult to change ⇒ can’t
keep up with the needs of thebusiness
Bad code/obsolete frameworks ⇒difficult to hire/retain good people
It’s a downwards spiral
Bug fixes and enhancements aren’t done
correctly
Design continues to degrade

Slide 14

Improvingpg the code
Dependency injection
Decouples components from one anotherand from the infrastructure code

Aspect-orientedi
d programmingi
Eliminates infrastructure code fromse ices
services
Implements it one place
Ensures DRY SOCs

Slide 15

POJO programming model
Application

Module A

Intro to POJOs

Module B

POJOs In Action© Chris Richardson - 2006

Module C

16

Agendag
Tangled code, tight coupling andduplication
Using dependency injection
Dependency injection with less XML
Simplifying
p y g code with aspects
pUsing aspects in the domain model

Slide 17

Dependencypy injectionjAccountService
BankingTransaction transfer(fromId, toId, amount)<<interface>>
Account
Dao<<interface>>
BankingTransaction
DaofindAccount(id)createTransaction(…)

JdbcAccount
Dao

JdbcBanking
Transaction
DaofindAccount(id)createTransaction(…)<<singleton>>
Jdbc
ConnectionManager<<singleton>>
Transaction
Manager

Application components
depend on:

One another
Infrastructure components

Old way:y componentspobtain dependencies:

Instantiation using new
Statics – singletons or static
methods
Service Locator such as JNDI

But these options result in:
Coupling
Increased complexity

New way: Passdependencies tocomponent:

Setter injection
Constructor injection18

Replacepinstantiation with injectionjpublic AccountServiceImpl(AccountDao accountDao,
BankingTransactionDao bankingTransactionDao) {this.accountDAO = accountDao;
this.bankingTransactionDAO = bankingTransactionDao;}

Slide 19

Replace singleton with dependencyjinjection
class AccountServiceImpl
p …
public BankingTransaction transfer(String
fromAccountId, String toAccountId,
double amount) {Extract
Local
Variable

TransactionManager.getInstance().begin();…
}BankingTransaction transfer(String
fromAccountId, String toAccountId,
double amount) {TransactionManager transactionManager =TransactionManager.getInstance();transactionManager.begin();…
}Convert Local Variable to Field

AccountServiceImpl(
AccountDao accountDao,
BankingTransactionDao
bankingTransactionDao) {tthis.accountDAO
s accou t O = accountDao;
accou t ao;
this.bankingTransactionDAO =bankingTransactionDao;
transactionManager =TransactionManager.getInstance();}

Introduce
Parameter

public AccountServiceImpl(
AccountDao accountDao,
BankingTransactionDao
bankingTransactionDao,
a sact oa age ttransactionManager)a sact oa age ) {TransactionManager
this.accountDAO = accountDao;
this.bankingTransactionDAO =bankingTransactionDao;
this.transactionManager = transactionManager;}

Slide 20

Replace static dependency withjinjection
BankingSecurityManager.verifyCallerAuthorized(AccountService.class, "transfer");

public class BankingSecurityManagerWrapper {public void verifyCallerAuthorized(Class<?> targetType, String methodName) {BankingSecurityManager.verifyCallerAuthorized(targetType, methodName);}}public AccountServiceImpl(…
BankingSecurityManagerWrapper bankingSecurityWrapper) {…
this.bankingSecurityWrapper = bankingSecurityWrapper;}

Slide 21

Who instantiates the objects?
jpublic class AccountServiceDelegate implements AccountService {Clients thati t ti t service
instantiateineed to pass independencies
B t they
But
th
could
ld usedependency injection
too ⇒ ripples up
through the code
We could use a handwritten factory but
that’sthats where Springcomes into play

public AccountServiceDelegate() {this service = newthis.service
AccountServiceImpl(
new JdbcAccountDao(),
new JdbcBankingTransactionDao(),}public class AccountServiceDelegate implements AccountService {public AccountServiceDelegate(AccountService service) {this.service = service;}}
public class SpringAccountServiceTests extends AbstractSpringTest {!

protected void onSetUp() throws Exception {super onSetUp();
super.onSetUp();
service = new AccountServiceDelegate(
new AccountServiceImpl(
new JdbcAccountDao(),
new JdbcBankingTransactionDao(),TransactionManager.getInstance(),
AuditingManager.getInstance(),di i()BankingSecurityManagerWrapper.getInstance()));}

Slide 22

The Springp g framework
Simplicity and power
SupportsSt th
the POJO programming
i
model
d l
Dependency injection
AOP for handling crosscutting concerns
Simplified APIs for many 3rd party
frameworks (Hibernate, JDBC, Quartz, JMX,..))
Web frameworks: MVC, WebFlow

Rapid
p evolution
Spring 2.0 – October 2006
Spring 2.5 – December 2007
C
Completel t b
backwardkd compatibility
tibilitSpringp g lightweightgg container
Lightweight container = sophisticatedfactory for creating objects
Spring bean = object created andmanaged by Spring
You write XML that specifies how to:
Create objects
Initialize them using dependency
injection24

Springp g code examplep<beans>public class AccountServiceImpl …
public AccountServiceImpl(
AccountDao
accountDao, …){
thi
this.accountDao
tD=
accountDao;…
}

public class JdbcAccountDao
implementslAccountDao {…
}<bean id="accountService"class="AccountServiceImpl"><constructor-arg ref=“accountDao"/>…
</bean><bean id=“accountDao"
class="JdbcAccountDao">…
/</bean></beans>25

Usingg Spring
p g dependencypy injection
j<beans><bean id=
id="AccountServiceDelegate"
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=<constructor-arg
ref="bankingTransactionDao"/>bankingTransactionDao /><constructor-arg ref="transactionManager"/><constructor-arg ref="auditingManager"/><constructor-arg ref="bankingSecurityManagerWrapper"/></bean><bean id<b
id="accountDao""
tD "class="net.chris...domain.jdbc.JdbcAccountDao"/>

ApplicationContext ctx =new ClassPathXmlApplicationContext("appCtx/banking-service.xml");
service = (AccountService) ctx.getBean("AccountServiceDelegate");.getBean(AccountServiceDelegate );<bean id="bankingTransactionDao"
class="net.chris...domain.jdbc.JdbcBankingTransactionDao"/><bean id="transactionManager" factory-method="getInstance"class="net.chris...infrastructure.TransactionManager"/><bean id="auditingManager" factory-method="getInstance"class="net.chris...infrastructure.AuditingManager"/><bean id="bankingSecurityManagerWrapper"class="net.chris...infrastructure.BankingSecurityManagerWrapper"/></beans>

Slide 26

Eliminatingg Java singletonsg
Springp g beans are
singletons (bydefault))
Spring caninstantiate classes
such as theTransactionManager(if all of its client’s
use Spring)

public class TransactionManager {public TransactionManager() {}public void begin() {…}<beans>….
<bean id=
id="transactionManager"transactionManager
factory-method=“getInstance”
class="net.chrisrichardson.bankingExample.infras
tructure.TransactionManager"/><bean id="auditingManager"ggfactory-method=“getInstance”
class="net.chrisrichardson.bankingExample.infras
tructure.AuditingManager"/></beans>

Slide 27

Revised designg

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

BankingSecurityManager
Wrapper

AccountService

Business Tiervoid create(Account)
BankingTransaction transfer(fromAccountId, toAccountId, amount)verifyCallerAuthorized()

AuditingManager
Account
accountIdbalanceoverdraftType<<interface>>
AccountDao
Account findAccount(accountId)

Account findAccount(accountId)

txnId
date

JdbcConnection
M
Manager
getConnection()cleanUp()

audit()

addTransaction()

JdbcBanking
TransactionDao

JdbcAccountDao
BankingTransaction<<interface>>
Banking
TransactionDao

addTransaction()dd()

TransactionManager
begin()g ()commit()rollback()Fast unit testingg exampleppublic class AccountServiceImplMockTests extends MockObjectTestCase {private AccountDao accountDao;
private BankingTransactionDao bankingTransactionDao;
private TransactionManager transactionManager;…

Create mockdependencies andinject themprotected void setUp() throws Exception {accountDao = mock(AccountDao.class);bankingTransactionDao = mock(BankingTransactionDao.class);transactionManager = mock(TransactionManager.class);
mock(TransactionManager class);…
service = new AccountServiceImpl(accountDao, bankingTransactionDao, transactionManager, auditingManager,bankingSecurityWrapper);}public void testTransfer_normal() throws MoneyTransferException {checking(newh ki (E
Expectations()
t ti
() {{
one(accountDao).findAccount("fromAccountId"); will(returnValue(fromAccount));one(accountDao).findAccount("toAccountId"); will(returnValue(toAccount));one(transactionManager).begin();…
}});
TransferTransactionfresultl = (TransferTransaction)(f) service.transfer("fromAccountId",
f ("fd" "toAccountId","d" 15.0);)
assertEquals(15.0, fromAccount.getBalance());
assertEquals(85.0, toAccount.getBalance());…
verify();}Slide 29

Springp g beans in ppractice

Slide 30

Configuring Spring beans in anpp
application
Web application<web>

ApplicationContext createdon startup
Web components can call
AppCtx.getBean()Some frameworks can
automaticallytti ll iinject
j tS
Springibeans into web components<context-param><<param-name>contextConfigLocation</param-name>>t tC fi Lti </><param-value>appCtx/banking-service.xml</param-value></context-param>…
</web>
ApplicationCtx ctx =WebApplicationContextUtils.
getWebApplicationContext(ServletContext)

Testing

Tests instantiate application
context
Call getBean()Better: Use
AbstractDepdendencyInjectionS
pringContextTests for

dependencyddinjection into
tests

AccountService service = (AccountService) ctx.getBean("AccountServiceDelegate");public class SpringAccountServiceTests extends
AbstractDependencyInjectionSpringContextTests {private AccountService service;…
@Override
protected String[] getConfigLocations() {return new String[] { "appCtx/banking-service.xml" };}public void setAccountServiceDelegate(AccountService service) {this.service = service;;}…
}

Slide 31

DemoLet’s walk through the revised code

Slide 32

Benefits of dependencypy injectionjSimplifies code
Promotes loose coupling
Makes testingg easier

Slide 33

Agendag
Tangled code, tight coupling andduplication
Using dependency injection
Dependency injection with lessXML
Simplifying code with aspects
Using aspects in the domain model

Slide 34

Dependencypy injectionjwith less XML
Spring 2.5
Annotation-based configuration
Class path component scanning

Spring JavaConfig
Java-based configuration of Spring beans

Arid DAO
Minimal XML
Automatically generated finders

Slide 35

Annotation-based configurationg
public class MoneyTransferServiceImpl implements MoneyTransferService {
private final AccountRepositoryppy accountRepository;pyprivate final BankingTransactionRepository bankingTransactionRepository;@Autowired<beans>public MoneyTransferServiceImpl(AccountRepository accountRepository,
BankingTransactionRepositoryk<context:annotation-config/>bankingTransactionRepository) {this.accountRepository = accountRepository;<bean
this.bankingTransactionRepository = bankingTransactionRepository; name="moneyTransferService"}class="MoneyTransferServiceImpl"/>
public class HibernateAccountRepository
implements AccountRepository {private HibernateTemplate hibernateTemplate;@Autowired
public HibernateAccountRepository(HibernateTemplate template) {hibernateTemplate = template;}<bean name="accountRepository"
class="HibernateAccountRepository"/></beans>

Annotation-based dependencyjinjection@Autowire annotation on
Setters
FieldsConstructors
Methods

Qualifiers for selecting one of multiple
candidates to autowire:@Qualifier("mainCatalog")
Bean definition contains <qualifier>Auto-detection of beans@Component
public class MoneyTransferServiceImpl
implementslMoneyTransferServicef{…
}@Component
public class HibernateAccountRepository
implements AccountRepository {…
}<beans><context:component-scan basepackage="net.chrisrichardson.bankingExample"/></beans>

Very little XML!

Componentpscanning
g in the classpathpConfigurable filters
Annotation
Regex
AspectJ
Regex

Naming beans:@Component("myService")
name-generator="MyNameGenerator"@Scope("prototype")

Usingg Springp g JavaConfig
g1@Configuration
public abstract class AppConfig {@Beanpublic MoneyTransferService moneyTransferService() {return new MoneyTransferServiceImpl(accountRepository(),bankingTransactionRepository());}@Beanpublic AccountRepository accountRepository() {HibernateAccountRepository repo = new HibernateAccountRepository();
repo.setSessionFactory(sessionFactory());
return repo;
p ;
}
@Bean
public BankingTransactionRepository bankingTransactionRepository() {HibernateBankingTransactionRepository repo = new HibernateBankingTransactionRepository();
repo.setSessionFactory(sessionFactory());
return repo;}@ExternalBean
p blic abstract
publicabst act SessionFactory
SessionFacto sessionFactory();
sessionFacto ()}Usingg Springp g JavaConfig
g2<bean><bean class="net.chrisrichardson.bankingExample.javaconfig.AppConfig"/><bean class="org.springframework.config.java.process.ConfigurationPostProcessor" /></beans>

Even simplerp
DAOs with Arid<beans><arid:define-beans
id d fibpackage='org.jia.ptrack.domain'
pattern='net.chrisrichardson.arid.domain.GenericDao+'…
</arid:define-beans>/ id d fib…
</beans>
public interface AuditEntryRepository extends
GenericDao<AuditEntry, Integer> {}List<AuditEntry> findByUserNameAndDateBetween(String username,username
Date fromDate, Date toDate)

Agendag
Tangled code, tight coupling andduplication
Using dependency injection
Dependency injection with less XML
Simplifying
p y g code with aspects
pUsing aspects in the domain model

Slide 43

Crosscutting
g concerns
Every service method:
Manages transactions
Logs entries and exits
Performs security
checksAudit logs

Tangled andduplicated code
OO does not enable ust writetoit thisthi coded inione placepublic class AccountServiceImpl implements AccountService {public BankingTransaction transfer(String fromAccountId, String toAccountId, double amount) {BankingSecurityManager.verifyCallerAuthorized(AccountService.class,"transfer");logger.debug("Entering AccountServiceImpl.transfer()");

Infrastructure

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:
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();Business Logic

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( ea sOpened < ffromAccount.getRequiredYearsOpen()omAcco nt getReq i edYea sOpen()|| 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,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);
TransactionManager.getInstance().commit();throw e;} finally {TransactionManager.getInstance().rollbackIfNecessary();}} }Infrastructure

Slide 44

Aspect-OrientedpProgramminggg ((AOP))
AOP

enables the modular implementation of crosscutting
concerns
i.e. eliminates duplicate code

AspectModule that implements a crosscutting concern
Collection of pointcuts and advice

Join point

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

PointcutSpecification of a set of join points
E g All public service methods
E.g.

AdviceCode to execute at the join points specified by a pointcut
g manageg transactions,, performpauthorization check
E.g.

Slide 45

Springp g AOP
Spring AOP = simple, effective AOP implementation
Li ht i ht container
Lightweightt i
can wrap objects
bj t withith proxiesi
Proxy masquerades as original object
Proxy executes extra advice:
Before invoking original method
After invoking original method
Instead of original method

Slide 46

Transaction Managementg
Aspectp@Aspect
public class TransactionManagementAspect {public class AccountServiceImpl …

private TransactionManager transactionManager;

public BankingTransaction transfer(
String fromAccountId,
String toAccountId, double amount) {…
transactionManager begin();transactionManager.begin();
…
try {
…

public TransactionManagementAspect(TransactionManager
transactionManager) {this.transactionManager = transactionManager;}@Pointcut("execution(public *net chrisrichardson *Service *( ))"))
net.chrisrichardson..*Service.*(..))
private void serviceCall() {}@Around("serviceCall()")
public Object manageTransaction(ProceedingJoinPoint jp)
throws Throwable {transactionManager begin();transactionManager.begin();transactionManager.commit();a a o a ago();…
} catch (MoneyTransferException e) {…
transactionManager.commit();throw e;} finally {transactionManager.rollbackIfNecessary();
}
}

try {
Object result = jp.proceed();transactionManager.commit();return result;} catch (MoneyTransferException e) {transactionManager commit();transactionManager.commit();throw e;} finally {transactionManager.rollbackIfNecessary();}}}

Slide 47

Springp g configurationg<beans><aop:aspectj-autoproxy /><bean id="transactionManagementAspect"class="net.chrisrichardson.bankingExample.infrastructure.aspects.TransactionManagementAspect"><constructor-arg ref="transactionManager"
ref= transactionManager /></bean></beans>

Slide 48

Logging
gg g Aspectp@Aspect
public class LoggingAspect implements Ordered {public class AccountServiceImpl …
private Log logger =LogFactory.getLog(getClass());
public BankingTransaction transfer(
String fromAccountId,
fromAccountId
String toAccountId, double amount) {…
logger.debug("Entering
AccountServiceImpl.transfer()");
…
try {
…
logger.debug("Leaving
AccountServiceImpl.transfer()");} catch (RuntimeException e) {logger.debug("Exception thrown in
AccountServiceImpl.transfer()",e);
throw e;}@Pointcut("execution(public((p*
net.chrisrichardson..*Service.*(..))")
private void serviceCall() {}@Around("serviceCall()")
public Object doLogging(ProceedingJoinPoint jp) throws
Throwable {Log logger = LogFactory.getLog(jp.getTarget().getClass());
Signature signature = jp.getSignature();String methodName = signature.getDeclaringTypeName()gg();+ ".“ + signature.getName();logger.debug("entering: " + methodName);try {Object result = jp.proceed();gg
g(g " + methodName););logger.debug("Leaving:
return result;} catch (Exception e) {logger.debug("Exception thrown in " + methodName, e);throw e;}}Slide 49

Auditingg Aspectppublic class AccountServiceImpl …
public BankingTransaction transfer(String
fromAccountId, String toAccountId,
double amount) {…

@Aspect
public class AuditingAspect {private AuditingManager auditingManager;
public AuditingAspect(AuditingManager auditingManager) {this.auditingManager = auditingManager;}@Pointcut("execution(public@P
i t t("ti ( bli *net.chrisrichardson..*Service.*(..))")
private void serviceCall() {}auditingManager.audit(AccountService.class,"transfer", new Object[] {fromAccountId, toAccountId, amount });@Before("serviceCall()")
public void doSecurityCheck(JoinPoint jp) throws Throwable{
auditingManager.audit(jp.getTarget().getClass(),jp.getSignature().getName(), jp.getArgs());}}

Slide 50

Securityy Aspectppublic class AccountServiceImpl …
public BankingTransaction transfer(
String fromAccountId,
String toAccountId, double amount) {…
public BankingTransaction transfer(String
fromAccountId, String toAccountId,
double amount) throws
MoneyTransferException {…
bankingSecurityWrapper.verifyCallerAuthorized(
AccountService.class,"transfer");…

@Aspect
public class SecurityAspect {private BankingSecurityManagerWrapper
bankingSecurityWrapper;
public SecurityAspect(BankingSecurityManagerWrapper
bankingSecurityWrapper) {thi b ki S
this.bankingSecurityWrapper
it W=bbankingSecurityWrapper;
ki S
it W
}
@Pointcut("execution(public *net.chrisrichardson..*Service.*(..))")
private void serviceCall() {}@Before("serviceCall()")
public void doSecurityCheck(JoinPoint jp) throws Throwable{bankingSecurityWrapper.verifyCallerAuthorized(jp.getTarget().getClass(), jp.getSignature().getName());}}

Slide 51

In picturesp

Transaction
Retryy Aspectp

Security Aspect

Transaction
Management
A
Aspectt

Logging
Aspectp

Auditing
Aspectp

AccountService

JDK/CGLib proxy

Slide 52

Simplerp
AccountService
public class AccountServiceImpl implements
AccountService {public AccountServiceImpl(
AccountDao accountDao,
BankingTransactionDao bankingTransactionDao) {this.accountDao = accountDao;
this bankingTransactionDao = bankingTransactionDao;
this.bankingTransactionDao}

Fewer dependencies

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;
Simpler code
switch ((fromAccount.getOverdraftPolicy())gy()) {
case Account.NEVER:
if (newBalance < 0)….
}

It's a POJO…

Slide 53

Simplerpmock objectj
test
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");
Fewer dependencies
will(returnValue(toAccount));
to mock
one(accountDao).saveAccount(fromAccount);one(accountDao).saveAccount(toAccount);
o
(a ou
ao) a
ou ( oou );one(bankingTransactionDao).addTransaction(
with(instanceOf(TransferTransaction.class)));}});TransferTransaction result = (TransferTransaction) service.transfer("fromAccountId", "toAccountId", 15.0);…
}

Slide 54

Transaction Retryy Aspectppublic class AccountServiceDelegate {@Aspect
public class TransactionRetryAspect {private Log logger = LogFactory.getLog(getClass());
private static final int MAX_RETRIES = 2;

private static final int MAX_RETRIES = 2;
public BankingTransaction transfer(String
fromAccountId, String toAccountId,
double amount) throws
MoneyTransferException {int retryCount = 0;
while (true) {try {returnu service.transfer(fromAccountId,
a
( o
ou d,
toAccountId, amount);} catch (ConcurrencyFailureException e) {if (retryCount++ > MAX_RETRIES)
throw e;}}}@Pointcut("execution(public *net.chrisrichardson..*Service.*(..))")
private void serviceCall() {}@Around("serviceCall()")
public Object retryTransaction(ProceedingJoinPoint jp)
throws Throwable {int retryCount = 0;logger.debug("entering transaction retry");whilehil (t(true)) {try {Object result = jp.proceed();logger.debug("leaving transaction retry");
return result;} catch (ConcurrencyFailureException e) {if (retryCount++ > MAX_RETRIES)
th
throw
e;
logger.debug("retrying transaction");
}
}
}

}
}

We can delete thedelegategclass!

Slide 55

Springp g IDE for EclipsepSlide 56

Demo
Step
p throughg the code

Slide 57

Spring
p gp
provided aspectspSpring framework provides important
infrastructure aspects
Transaction Management
TransactionInterceptor
PlatformTransactionManager

Spring Security a.k.a Acegi Security
MethodSecurityInterceptor

Slide 58

Agendag
Tangled code, tight coupling andduplication
Using dependency injection
Simplifying code with aspects
Dependencypy injectionjwith less XML
Using aspects in the domain
model

Slide 59

Using Aspects in the Domain
Model
Spring AOP works well for the service
layer
But it has limitations:Objects must be created by Spring
Can only intercept calls from outside
Only efficient when method calls are
expensivei

Inappropriate for domain modelcrosscutting concerns:
E.g. tracking changes to fields of domain
objects
Slide 60

Introduction to AspectJ
p
What is AspectJ

Adds aspects to the Java language
Superset of the Java language

History
Originally created at Xerox PARC
Now an Eclipse
p pprojectjUses byte-code weaving

Advice inserted into application codeDDone
at eitheri h compile-timeil ior lload-time
d iIncredibly powerful: E.g. intercept fieldsets and getsg61

Changeg tracking
gp
problem – old way
ypublic class Foo {pprivate Map<String, ChangeInfo> lastChangedBy= new HashMap<String, ChangeInfo>();publicbli void
id noteChanged(Stringt Chd(St iwho,hStringSt ifieldName)fi ldN) {lastChangedBy.put(fieldName, new ChangeInfo(who, new Date()));}•Put in a base class•Unless you run into singlepublic Map<String, ChangeInfo> getLastChangedBy() { inheritance restriction
return lastChangedBy;}
private int x;
private int y;public void setX(int newX) {noteChanged(determineCallerIdentity(), "x");this.x = x;}

• Call noteChanged() whenever a
field value is changed.
• Tangled code
• Error prone – too easy to forget62

Changeg tracking
gp
problem – AOP wayy@Observablepublic class Foo {@Watchprivate int x;
Now it's a simple POJO again

private int y;public void setX(int newX) {this.x = x;}63

Changeg tracking
g aspectppublic aspect ChangeTrackingAspect {declare parents: (@Observable *) implements Trackable;
private Map<String, ChangeInfo> Trackable.lastChangedBy= new HashMap<String, ChangeInfo>();private void Trackable.noteChanged(String who, String fieldName) {lastChangedBy.put(fieldName, new ChangeInfo(who, new Date()));}
public Map<String, ChangeInfo> Trackable.getLastChangedBy() {return lastChangedBy;}…

Adds the Trackable interface to all classes
annotatednnot ted with
ith @Observable@Obbl
Adds these members to all classes that
implementp
the Trackable interface

POJOs In Action
Copyright © Chris Richardson - 200664
Spring AOP

Trackingg field sets…
private SecurityInfoProvider securityInfoProvider;
pointcut fieldChange(Trackable trackable, Object newValue) :set(@Watch * Trackable+.*) && args(newValue) && target(trackable);after(Trackable trackable, Object newValue) returning() :fieldChange(trackable, newValue) {FieldSignature signature =(FieldSignature)thisJoinPointStaticPart.getSignature();String name = signature.getField().getName();String who = provider.getUser();
F
Foo
f
foo
= new Foo();F ()trackable.noteChanged(who, name);
foo.setX(1);}foo.setY(2);<bean} id="changeTracker"
p
p
p
g
g p
class="net.chrisrichardson.aopexamples.simple.ChangeTrackingAspect"
factory-method="aspectOf"><property name="provider" ref="securityInfoProvider"/></bean>Trackable trackable = foo;…

<bean id="securityInfoProvider"class="net.chrisrichardson.aopexamples.simple.SecurityInfoProvider“//>

POJOs In Action
Copyright © Chris Richardson - 2006

65
Spring AOP

Benefits of AOP
Incredibly powerful
Modularizes crosscutting concerns
Simplifies application code
Decouples application code from
infrastructureT
Two
options:ti
Spring AOP – simple but less powerfulA
AspectJJ – powerful
f l butb withi h a pricei

Slide 66

Drawbacks of AOP
Cost of using AspectJ
Compile-time weaving – changes build
Load-time weaving – increases startup
timeti

Not everyone’s idea of simplicity
Code no longer explicitly says what to do

Slide 67

SummaryyDependency
injection
Aspect-oriented
Programming
Object/relational
mapping

Improvedp o ed SOC
DRY codepcode
Simpler
Improved
maintainability
Easier to develop
and test
Let’s you focus on
the core problem

Slide 68

For more information
Buyy myy book ☺
Send email:chris@chrisrichardson.net
Visit my website:
http://www.chrisrichardson.net
Talk to me about consultinggand training
Download Project Track,
ORMUnit, etc
http://code.google.com/p/projecttrack/
http://code.google.com/p/aridpojos
http://code.google.com/p/ormunit
http://code.google.com/p/umangite