390 likes | 579 Views
Spring AOP – Part 2. Advanced Aspect-Oriented Programming. Spring’s Advice Interfaces (1). Spring provides interfaces that can be used to implement advice. The advice can then be added either: Programmatically with the ProxyFactory Declaratively with a ProxyFactoryBean
E N D
Spring AOP – Part 2 Advanced Aspect-Oriented Programming
Spring’s Advice Interfaces (1) • Spring provides interfaces that can be used to implement advice. • The advice can then be added either: • Programmatically with the ProxyFactory • Declaratively with a ProxyFactoryBean • Prior to Spring 2.0, this was the most common way to introduce custom AOP • The namespace-based and annotation-driven approaches make use of these interfaces internally.
Spring’s Advice Interfaces (2) • Before implement MethodBeforeAdvice: void before(Method method, Object[] args, Object target) throws Throwable;
Spring’s Advice Interfaces (3) • After implement AfterReturningAdvice: void afterReturning(Object retval, Method method, Object[] args, Object target) throws Throwable;
Spring’s Advice Interfaces (4) • After Throwing implement ThrowsAdvice: NO REQUIRED METHODS TO IMPLEMENT 2 options: public void afterThrowing(Method method, Object[] args, Object target, MyException e) {..} OR… public void afterThrowing(MyException e) {..}
Spring’s Advice Interfaces (5) • Around implement org.aopalliance.intercept.MethodInterceptor: public abstract Object invoke(MethodInvocation invocation) throws Throwable;
Using Advice with the ProxyFactory Create the ProxyFactory private MessageService getMessageService(List obscenities) { MessageService target = new MessageServiceImpl(); ProxyFactory pf = new ProxyFactory(target); pf.addAdvice(new ObscenityFilterAdvice(obscenities)); return (MessageService) pf.getProxy(); } Create and return the proxy This version of the ObscenityFilterAdvice would implement the MethodBeforeAdvice interface
Invoking Advice via ProxyFactoryBean Specify the target <bean id="target" class="com.springsource.training.aop.MessageServiceImpl"/> <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target“ ref=“target”/> <property name="proxyInterfaces"> <value>com.springsource.training.aop.MessageService</value> </property> <property name="interceptorNames"> <value>advice</value> </property> </bean> <bean id="advice" class="com.springsource.training.aop.after.ReturnCheckingAdvice"/> Specify the interface(s) the proxy should implement Specify the advice to add to the proxy
Out of the Box Interceptors • You may not need to implement AOP advice yourself • Spring provides interceptors out of the box for • Transaction management • Accessing remote resources • Accessing JMX resources • Rich potential for aspect libraries • Spring is a key part of this effort
Understanding Introductions • Introductions allow you to add new behavior to a bean dynamically at runtime • In Spring AOP, this means making objects implement additional interfaces • Allows more complex cross-cutting logic to be applied • Locking • Modification checking • Mixin • Essentially a more dynamic form of concrete inheritance • Typically adds state to target on a per-instance basis • Inter-type declarations • Another name for introductions • Preferred by AspectJ community
Modification Detection with Introductions (1) • Create a ModificationTracker interface: public interface ModificationTracker { public boolean isModified(); public void setModified(boolean modified); }
Modification Detection with Introductions (2) • Create an implementation: • The state and behavior behind the interface public class DefaultModificationTracker implements ModificationTracker { private boolean isModified; public boolean isModified() { return isModified; } public void setModified(boolean modified) { this.isModified = modified; } }
Modification Detection with Introductions (3) • Use <aop:declare-parents> to wrap Contacts with proxy which implements ModificationTracker: <aop:config> <aop:aspect id="introductionsExample"> <aop:declare-parents types-matching="com.springsource.training.aop.introductions.Contact+" implement-interface="com.springsource.training.aop.introductions.ModificationTracker" default-impl="com.springsource.training.aop.introductions.DefaultModificationTracker"/> </aop:config> <bean id="contact" class="com.springsource.training.aop.introductions.SimpleContact"> <property name="firstName" value="Ben"/> <property name="lastName" value="Hale"/> </bean> Apply to any implementation of the Contact interface
Modification Detection with Introductions (4) • Create around advice to use new interface: public class ModificationTrackingIntroduction { public void trackModifications( ProceedingJoinPoint pjp, ModificationTracker mixin) throws Throwable { boolean modified = mixin.isModified() || hasModification(pjp.getSignature().getName(), pjp.getTarget(), pjp.getArgs()); mixin.setModified(modified); pjp.proceed(); … Remember to callproceed()
Modification Detection with Introductions (5) • Apply around advice: <bean id="introduction" class="com.springsource.training.aop.introductions.ModificationTrackingIntroduction"/> <aop:config> <aop:aspect id="introductionsExample" ref="introduction"> <aop:declare-parents types-matching="com.springsource.training.aop.introductions.Contact+" implement-interface="com.springsource.training.aop.introductions.ModificationTracker" default-impl="com.springsource.training.aop.introductions.DefaultModificationTracker"/> <aop:around method="trackModifications" pointcut="execution(void set*(*)) and this(mixin)"/> </aop:aspect> </aop:config> <bean id="contact" class="com.springsource.training.aop.introductions.SimpleContact"> <property name="firstName" value="Ben"/> <property name="lastName" value="Hale"/> </bean> Pointcut expression matches setter methods
Demo Modification Detection with Introductions
Key configuration options • Key configuration options for Spring proxies, inherited from the ProxyConfig class. • Apply to auto proxy creators as well as ProxyFactoryBean and conveniences such as TransactionProxyFactoryBean
Interceptor and proxy lifecycle • Can advise singletons or prototypes (in Spring IoC terminology) • Each advice can be a singleton (shared) or prototype (per-instance) • Can mix shared and per-instance advice • Shared advice is most common • Most enterprise advice is stateless, such as transaction advice • Prototype advice will only make a difference if you advise several singletons or advise a prototype
A note on self-invocation Consequences of having distinct proxies and targets • If a target returns this, the proxy wraps it automatically • If a target wants to invoke itself but with advice applying, it’s necessary to • Set exposeProxy config property to true • Invoke result of AopContext.currentProxy() • Similar to getEJBObject()
Interrogating and changing proxy state • By default, objects advised by Spring AOP can be cast to the Advised interface • Advised a = (Advised) myProxy; • Can query advice chain • toProxyConfigString() returns useful diagnostic information • Can even add or remove advisors • Useful in tests, but questionable in a deployed application • Try casting to Advised in the labs!
Other dynamic runtime behavior enabled by AOP • Spring AOP is also used to enable Spring to deliver hot swapping and pooling support with no need to modify Java code • Done through ability to define a TargetSource, enabling target to be changed at runtime, to be taken from a pool etc. • TargetSource is used to obtain the joinpoint instance for each invocation • The default TargetSource used is SingletonTargetSource, with a static target • In some cases there will be no target • Terminal interceptor does all the work • Example: Calling out to a remote object in Spring proxy implementations
@Configurable (1) • It is possible to apply dependency injection to objects that are not explicitly managed by the Spring IOC container. • For example, calling new() to construct a domain instance as opposed to using getBean(..) to retrieve a prototype bean. • An aspect from spring-aspects.jar makes this possible. • Enable weaving by setting the –javaagent VM arg with the full path to aspectjweaver.jar
@Configurable (2) @Configurable publicclass WeatherMonitor { private String location; private WeatherService weatherService; public WeatherMonitor(String location) { this.location = location; } publicvoid setWeatherService(WeatherService) { this.weatherService = weatherService; } public String getWeatherReport { return weatherService.getCurrentSummary(location); }
@Configurable (3) Class whose instances require Dependency Injection <aop:spring-configured/> <bean class="com.i21.WeatherMonitor" scope="prototype"> <property name="weatherService" ref="weatherService"/> </bean> <bean id="weatherService" class="com.i21.StubWeatherService"/> Scope must be prototype DI as usual
@Configurable (4) public static void main(String[] args) { new ClassPathXmlApplicationContext(“beans.xml”); WeatherMonitor monitor = new WeatherMonitor("Chicago"); System.out.println(monitor.getWeatherReport()); } public String getWeatherReport() { return weatherService.getCurrentSummary(location); } weatherService property was provided via dependency injection
@Configurable (5) • By enabling DI for fine-grained instances, @Configurable promotes a rich domain model. • It is a much better approach than the Singleton anti-pattern. • For unit testing, simply run without the AspectJ weaver. • @Configurable will have no effect • Set dependencies programmatically • Use mocks and/or stubs as needed
Demo @Configurable
AspectJ Integration (1) • AspectJ is a programming language • Compiles classes + aspects to bytecode • or link (weave) aspects with existing .class files • No proxies are used • No distinction between target and proxy • Cannot apply different advice to same class • Comprehensive join point model • Method call/execution • Field read/write • Object initialization • Static initialization
AspectJ Integration (2) • AspectJ itself has no DI capabilities • Spring adds DI to AspectJ • Currently only for singleton aspects • Use @Configurable for non-singletons • Configuring AspectJ aspects in Spring is just like configuring Spring aspects
Using Spring and AspectJ (1) • Consider the following business object: public class Account { private int balance; private int overdraft; public int withdraw(int amount) throws InsufficientFundsException { int newBalance = balance - amount; if (newBalance < -overdraft) { throw new InsufficientFundsException(); } balance = newBalance; return balance; } public int deposit(int amount) { balance += amount; return balance; } /* getters and setters omitted */ }
Using Spring and AspectJ (2) • Now consider this AspectJ aspect: public aspect SecurityManagerAspect { private SecurityManager securityManager; public void setSecurityManager(SecurityManager securityManager) { this.securityManager = securityManager; } private pointcut balanceChange() : set(int Account.balance); before() : balanceChange() { this.securityManager.checkCanModify(); } } Standard JavaBeans setter Define a pointcut for setting the balance field of the Account class Before modifying the balance field, use the SecurityManager to authorise the action
Using Spring and AspectJ (3) • SecurityManager is a standard Java interface: public interface SecurityManager { void checkCanModify(); } • We have a stub implementation: public class StubSecurityManager implements SecurityManager { public void checkCanModify() { System.err.println("Checking security"); } }
Using Spring and AspectJ (4) • Configure the aspect in Spring: <beans> <bean id="securityManager" class="...aop.aspectj.StubSecurityManager"/> <bean id="securityAspect" class="...aop.aspectj.SecurityManagerAspect" factory-method="aspectOf"> <property name="securityManager"> <ref local="securityManager"/> </property> </bean> </beans> Configure the SecurityManager Retrieve the aspect instance Inject the SecurityManager
Demo Integrating with AspectJ
Summary (1) • Spring supports a limited core of AOP • Interceptor based approach • Extensive integration with AspectJ if needed • Can reuse AOP Alliance aspects • Spring provides important prepackaged aspects. This allows you to take advantage of AOP without having to know much about it • Transactions • Security • Remoting • And others • Can gradually start adding in custom AOP as desired
Summary (2) • Spring AOP supports method execution join points and the standard advice types: around, before, after-returning, after-throwing, after • Spring AOP supports introductions • Make proxy implement additional interfaces beyond those implemented by target class • Spring AOP offers several ways to create a proxy: • XML-based configuration with the AOP namespace. • Annotation-based with @AspectJ style configuration. • For an individual bean (or pre-Spring 2.0), use ProxyFactory of ProxyFactoryBean
Summary (3) • Spring 2.0+ enables important AspectJ features at runtime within the Spring AOP proxy-based model (and without the ajc compiler): • AspectJ pointcut expression language • @AspectJ annotation-based syntax • @Configurable annotation used by an AspectJ aspect to enable dependency injection for objects not instantiated by a Spring container. • Combines ease-of-adoption of Spring AOP with de facto standard of AspectJ