Using Advanced Features of Spring s AOP Framework


Using Advanced Features of Spring's AOP Framework

Let's now look at some more advanced uses of Spring's AOP framework.

TargetSources

Typically, the interceptor chain concludes by using reflection to invoke the appropriate method on the target object. This is the target object's implementation of the method. Typically, there is a target object that is known when the proxy is constructed. The point of using Spring AOP is normally to add declarative behavior around a target object.

However, the way in which Spring actually terminates the interceptor chain is a bit more sophisticated. Spring obtains the target object reference just before it invokes the appropriate method. The target object is obtained from an implementation of the TargetSource interface. This interface is as follows:

public interface TargetSource {        Class getTargetClass();        boolean isStatic();        Object getTarget() throws Exception;        void releaseTarget(Object target) throws Exception;     }

In the simplest, commonest case, where there's a target object known in advance, Spring creates an implementation of the TargetSource interface without the developer needing to be aware of it. This implementation (SingletonTargetSource) will return the same instance in all cases, when the getTarget() method is invoked.

However, the TargetSource interface also allows for more sophisticated behavior. For example, the target instance can be obtained from a pool or a factory.

Spring comes with a number of TargetSource implementations out of the box, and you can easily implement your own TargetSource.

HotSwappableTargetSource

The most important TargetSource implementation — other than the default implementation used for "singleton" targets — is HotSwappableTargetSource. This enables us to switch the target object under a proxy in a threadsafe manner. This capability is particularly important in a Dependency Injection framework. Let's suppose that object A expresses a dependency on interface B via Dependency Injection. How do we retain the option of changing the implementation of B in a manner transparent to A? To do this, the implementation of B supplied by the container to satisfy the dependency must be a proxy — and the logical approach is for it to be a proxy created by Spring's AOP framework, enabling the option of using other AOP capabilities. Such proxies will use HotSwappableTargetSource.

Important 

By default, hot swapping is not enabled, and Spring will satisfy dependencies with an unadorned POJO. This provides optimum performance and is appropriate in most cases. Explicit configuration is required to enable hot swapping.

The HotSwappableTargetSource class offers one new method:

 Object swap(Object o); 

This returns the old target object.

Typical use of the HotSwappableTargetSource will be as follows. We define an object to be the initial target, and construct a proxy using the HotSwappableTargetSource. We can then swap programmatically at any time.

<bean  > </bean>         <bean  > </bean>         <bean     >   <constructor-arg><ref local="target1"/></constructor-arg> </bean>             <bean       >        <property name="targetSource"><ref local="swapper"/></property> </bean>

Programmatic swapping is shown here:

MyInterface target1 = (MyInterface) beanFactory.getBean("target1"); MyInterface target2 = (MyInterface) beanFactory.getBean("target2");     MyInterface proxied = (MyInterface) beanFactory.getBean("swappable"); // Invocations on proxied now hit target1     HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper"); Object old = swapper.swap(target2); // old will be target1     // Invocations on proxied now hit target2

In this case, both targets come from bean definitions. It's quite likely that the initial target would come from a bean definition and that a new target might come from another source.

Important 

The HotSwappableTargetSource is ideal for building infrastructure — for example, to support dynamic reconfiguration of certain application objects that change frequently.

Pooling Target Source

Another important use of the TargetSource interface is to support pooling. In this case, you don't want a single target — you want a pool of targets, with the actual call at the end of the interceptor chain going to a free target. Note that the interceptor instances will normally be shared. This is appropriate; typically the interceptors will be threadsafe, such as transaction interceptors.

The proxy itself will be a singleton instance, and stands in front of a pool of target objects.

Use Out of the Box

Out of the box, Spring supports Apache Commons Pool (http://jakarta.apache.org/commons/pool/). This is an open source pool implementation with acceptable performance, which is robust under load.

You can configure a pool for any POJO as follows:

<bean     singleton="false">  <property name="count"><value>10</value></property> </bean>     <bean  >  <property name="targetBeanName"><value>prototypeTest</value></property>  <property name="maxSize"><value>25</value></property> </bean>         <bean             >   <property name="targetSource"><ref local="poolTargetSource"/></property>   <property name="interceptorNames"><value>whatever...</value></property>     </bean>

Note that while pooled is a singleton bean definition, the target prototypeTest must be a non-singleton definition, to allow the pool that Spring will configure to instantiate independent instances as necessary.

There are no constraints on the actual classes that can be pooled; while the pooling model is similar to that for stateless session EJBs, it applies to any POJO.

Use with Your Own Choice of Pooling Implementation

Like everything else in Spring, the pooling TargetSource support allows for customization. You can extend org.springframework.aop.target.AbstractPoolingTargetSource to use a pool provider other than Commons Pool. It's necessary to implement the following methods:

 protected final void createPool(BeanFactory beanFactory); 

This method should be used to create the pool. As usual, dependencies can be specified using Dependency Injection.

 public Object getTarget() throws Exception; 

This method should return an object obtained from the pool.

 public void releaseTarget(Object target) throws Exception; 

This method should return an object to the pool after an invocation has finished with it.

 public void destroy(); 

This method should shut down the pool before the TargetSource is destroyed.

Any pool API will normally require a callback to construct a new object for the pool. This should be implemented using the inherited newPrototypeInstance() method, as shown in the following example from the Commons Pool implementation:

public Object makeObject() {     return newPrototypeInstance(); }

Basic pool configuration parameters such as maxSize are inherited from org.springframework.aop.target.AbstractPoolingTargetSource; you should add properties for any pool-specific configuration.

When Should You Use Pooling?

Spring's AOP-based pooling capability is most often used as an alternative to local stateless session EJBs. It provides the same basic model: a pool of stateless service objects, any of which may be invoked by a given method invocation. However, stateless service objects are typically easy to make threadsafe; there's usually no need for read-write instance variables — or at least instance variables that are read-write after configuration is complete. And it's usually more convenient to have a single instance of an object than a pool; consider the case in which you want to maintain a cache of objects.

Important 

Don't be too eager to use pooling. It's often unnecessary. See Chapter 12 of J2EE without EJB for a discussion of the tradeoffs involved in pooling. Pooling is an overvalued idea in J2EE architecture, given the efficient garbage collection of modern JVMs.

Exposing Pool Statistics

If you want to expose information about pool statistics for a pooled object, you can do so as follows:

<bean  >  <property name="targetObject"><ref local="poolTargetSource" /></property>  <property name="targetMethod"><value>getPoolingConfigMixin</value></property> </bean>     <bean     >  <property name="targetSource"><ref local="poolTargetSource"/></property>      <property name="interceptorNames"><value>poolConfigAdvisor</value></property>      <!-- Necessary as have a mixin and want to avoid losing the class,           because there’s no target   <property name="proxyTargetClass"><value>true</value></property>     </bean> 

This uses an introduction (discussed later in this chapter) to cause the AOP proxy in front of the pool to implement the PoolingConfig interface.

You can now cast any object obtained from the pool to PoolingConfig and interrogate its statistics as follows. We assume that the pooled object is a reference to the AOP proxy in front of the pool:

PoolingConfig conf = (PoolingConfig) pooled;  assertEquals(1, conf.getInvocations());  assertEquals("Correct target source", 25, conf.getMaxSize());

This is an interesting example of the potential of introduction, discussed in the next section.

Custom TargetSources

You are also free to implement your own TargetSource.

Consider a recent question that came up in one of the Spring lists. A user needed to use several datasources, selecting one at runtime based on the context.

The traditional OO way to address this would be to implement a DataSource decorator that delegated to one or the other DataSource, depending on the state. As there aren't a huge number of methods on the DataSource interface, this wouldn't be too painful. However, it's verbose and a good example of crosscutting.

You could meet this requirement by implementing a TargetSource that chose the correct DataSource based on context — the state of other application objects (which the custom TargetSource could access through Dependency Injection), the current user, or any other contextual information. Any interception chain would be shared, whatever the target the TargetSource chose at runtime.

Note 

You could also address this problem using the Method Injection feature of the core Spring IoC container. That approach would have the advantage that the object could itself invoke a getDataSource() method. You would need to implement a custom MethodReplacer in this case (see Chapter 2).

Programmatic Access to the TargetSource

It's possible to obtain, and even modify, the TargetSource from any AOP proxy by casting to Advised and using the TargetSource property. This is most useful with TargetSource implementations such as HotSwappableTargetSource that permit programmatic configuration changes.

In most cases, you won't have set the TargetSource explicitly, and the TargetSource will be of type SingletonTargetSource.

Important 

The TargetSource will never be null, even in the special case when there is no target object.

Doing Without a Target Object

Occasionally, there will be no target object. In this case, an interceptor will be responsible for returning the result. The TargetSource will be the canonical instance of org.springframework.aop.target.EmptyTargetSource, which always returns null from its getTarget() method.

You can dispense with a target in cases when all behavior is supplied by interceptors — possibly including IntroductionInterceptors, which often themselves hold a target. The most common case is when a terminal interceptor uses a strategy other than reflective invocation of a target object to call out of the interceptor stack. The Spring EJB proxies, such as org.springframework.ejb.access.LocalSlsbInvoker Interceptor, illustrate this approach: Instead of calling proceed(), which would cause the AOP infrastructure to reflectively invoke a target obtained from the TargetSource in the case of the last interceptor in the chain, they invoke the method specified in the MethodInvocation on a local or remote EJB. The same technique is used for remoting technologies.

Important 

Remember that an interceptor designed to call out of the interceptor chain, rather than call proceed, should normally be the last in the interceptor chain.

It's also possible, but more unusual, to have interceptors themselves implement methods. Any interceptor in the chain can choose to return a value (or throw an exception) rather than invoke the proceed() method.

Note 

You cannot dispense with a target object when using CGLIB proxying. See the discussion of proxy types later in this chapter.

Terminating the Interceptor Chain Early

When you do without a target — or for any other reason — you may want an advice in the chain to terminate early. This can happen in two ways: through throwing an exception or returning a value instead of calling proceed. Any advice can throw an exception, which will be reported to the client. Only Method Interceptors can return rather than call proceed(). Other advice types such as MethodBeforeAdvice don't have the ability to alter the return value.

Important 

What happens if a MethodInterceptor or other advice throws an exception? If it's not a checked exception, the exception will be seen by the client. If it's a checked exception on the signature of the method, the client will see that exception. If it's a checked exception not on the signature of the method, it will be wrapped in java.lang.reflect.UndeclaredThrowableException and reported to the client.

Using Introduction

Introduction means making an advised object (in this case, the Spring AOP proxy) implement additional interfaces, which are not implemented by the target object. It's even possible to create a proxy that consists solely of introductions, with no target object.

Introduction has many uses:

  • To create a mixin, adding to the state held in the object. This is probably the most important use.

  • To expose additional states associated with a special TargetSource. This is used within Spring, for example with scripting support.

  • To expose an object or object graph in a different way — for example, making an object graph implement the XML DOM interfaces.

    Important 

    Introduction is known as "intra-type declarations" in AspectJ terminology.

Spring provides two ways of making introductions:

  • The IntroductionInfo interface: Any advice can implement this to indicate that it introduces one or more interfaces. Such advices can be used with any pointcut, or even without a pointcut.

  • The IntroductionAdvisor interface: A special advisor type for introductions that doesn't include a MethodMatcher, which is not relevant for introductions. This actually extends the IntroductionInfo interface.

The first is the simpler approach and has been recommended since its introduction in Spring 1.1.1. (In earlier versions of Spring an IntroductionAdvisor was always required in order to make an introduction.)

Let's now look at a realistic example. Suppose we want to make any object "lockable," making it implement a Lockable interface to expose and control the locking state, and disabling all its setter methods when its state is locked.

The Lockable interface might look as follows:

public interface Lockable {     void lock();     void unlock();     boolean locked(); }

We now need an advice that makes the introduction. This must implement the IntroductionInfo interface, which contains the following method:

 Class[] getInterfaces() 

This method is implemented to return the interfaces that are introduced by the advisor or advice.

Typically, we will extend Spring's convenient DelegatingIntroductionInterceptor class when implementing an introduction advice. This class automatically detects any interface that a subclass implements and returns that in the IntroductionInfo.getInterfaces() method.

Thus our simple introduction can look like this:

public class LockMixin extends DelegatingIntroductionInterceptor              implements Lockable {             private boolean locked;         public void lock() {         this.locked = true;     }         public void unlock() {         this.locked = false;     }         public boolean locked() {         return this.locked;     }         public Object invoke(MethodInvocation invocation) throws Throwable {         if (locked() &&                   invocation.getMethod().getName().indexOf("set") == 0)             throw new LockedException();         return super.invoke(invocation);     } }

Not much code, considering that we can use this to enable locking on any object! But this is actually a fairly complex example of a DelegatingIntroductionInterceptor subclass. It overrides the invoke() method to add a guard to setter methods that are not covered by the introduction. Because an introductionis normally made by an interceptor, it has the ability to perform around advice also. A simpler DelegatingIntroductionInterceptor subclass would not override the invoke() method implemented by DelegatingIntroductionInterceptor but would merely implement the interfaces to be introduced.

Note 

It is possible to use DelegatingIntroductionInterceptor with an arbitrary delegate without concrete inheritance by setting its delegate property to any object. The default value is this, meaning the subclass.

We can now add this advice to a proxy configuration, in an interceptor chain (using the interceptorNames property) or programmatically, as shown here:

ProxyFactory pc = new ProxyFactory(new Class[] {Target.class }); pc.setTarget(myTarget); pc.addAdvice(new LockMixin());

We will now have a LockMixin instance associated with the target object.

In our second approach, using an IntroductionAdvisor, we need an advisor wrapper for this mixin. This gives us the opportunity to specify a ClassFilter to target the introduction, although we don't in this simple example:

public class LockMixinAdvisor extends DefaultIntroductionAdvisor {         public LockMixinAdvisor() {         super(new LockMixin(), Lockable.class);     } }

Note 

DefaultIntroductionAdvisor is concrete, so it can also be used declaratively in the same way as DefaultPointcutAdvisor.

We can use this advisor as follows:

ProxyFactory pc = new ProxyFactory(new Class[] {Target.class }); pc.setTarget(myTarget); pc.addAdvisor(new LockMixinAdvisor()); 

Important 

The only reasons for using an introduction advisor, rather than simply an introduction advice, are to apply a ClassFilter to limit where the introduction applies, or to narrow the number of interfaces that an advice implementing IntroductionInfo introduces. If neither of these applies, you should just use an introduction advice.

Let's look beyond the mechanics and think about the effect of our use of introduction in the example we've just seen. We have modularized an important crosscutting concern. We can now add locking behavior to any object without the need to modify that object. This is stateful advice; we need one advice instance per advised object.

Important 

It's possible to build applications using introductions. However, there's a danger here of recreating multiple inheritance in Java. Mixins are probably best restricted to infrastructure, such as the locking aspect we've just seen, or security aspects.

Note 

While Spring provides good support for introductions, in some cases you should consider using AspectJ to perform introductions. AspectJ can actually add fields to the advised classes, eliminating the need for a distinct mixin object for each instance for which the introduction applies.

Exposing the Current Proxy

You may have figured by this point that because of Spring's distinction between target object and AOP framework, the target object doesn't know anything about the AOP framework. While this is a good thing in many cases, it does have one unfortunate consequence: If a target object invokes a method on itself, it doesn't get advised unless the programmer takes special care. For example, if we have an object called AccountManager, and any other object has a reference to AccountManager via Spring IoC, the methods will be advised appropriately. If, however, an advised AccountManager implementation invokes a method on itself, it will simply be invoking a method on an unmodified class. It won't be advised. This situation might arise, for example, if a coarse-grained method in an AccountManager implementation requires several transactions and attempts to use declarative transaction management through calling multiple transactional methods.

This is exactly the same as the situation with EJBs, which must use the getEJBObject() method on the javax.ejb.SessionContext interface if they want to invoke methods on themselves.

To be able to invoke the target object through the advice chain, you must:

  • Configure the proxy factory to set the exposeProxy flag to true. The default is false, as the necessary ThreadLocal binding adds a small performance hit.

  • Use the AopContext.currentProxy() method to obtain the current proxy.

Then you will write code like this, to invoke the doSomething() method on the current object through the advice chain:

 ((MyObject) AopContext.currentProxy()).doSomething(); 

The downside here is that we have a dependence on Spring. In general we want to avoid dependencies on the framework. You will typically want to refactor the dependence on the AopContext class into a protected method so that you can override it at test time. This way you can unit test without always making invocations through the Spring AOP framework.

Note 

You might be thinking that it's inelegant that Spring forces you to explicitly access the proxy. It is, butit's a consequence of the tradeoffs in Spring's AOP implementation. If this is a real problem for you, it may be an indication to consider AspectJ or AspectWerkz to address the requirement in question.

Exposing the Current MethodInvocation

While the need to expose the AOP proxy is uncommon, it does arise from time to time. More rarely, we need to expose the MethodInvocation to objects within an AOP invocation that don't have access to it. Normally the only objects that need access to the MethodInvocation are AOP Alliance MethodInterceptors. As interceptors are passed the MethodInvocation in the arguments to the invoke method, they don't have a problem. However, what if a target object needs to see the invocation, or a pointcut? This isn't a common requirement, but it's not unheard of. For example, a target object may need to know how it's being used in an AOP context; a pointcut may need to access information available only through the MethodInvocation.

In this case, add the canonical instance of org.springframework.aop.interceptor.ExposeInvocationInterceptor to the beginning of the interceptor chain to expose the invocation, and use its static currentInvocation() method to obtain the current MethodInvocation. This value will be set correctly in the event of nested invocations.

If you're using programmatic proxy creation, or modifying an existing Advised object, configuration is trivial:

 advised.addAdvice(ExposeInvocationInterceptor.INSTANCE); 

It's a little harder when using XML configuration. Because ExposeInvocationInterceptor enforces the singleton pattern through exposing an INSTANCE field containing the canonical instance, you can't create an instance using new, as a normal Spring bean definition would do.

You need to use the FieldRetrievingFactoryBean (see Chapter 3) to get a reference to the canonical instance of ExposeInvocationInterceptor, as follows:

<bean     >   <property name="targetClass">       <value>org.springframework.aop.interceptor.ExposeInvocationInterceptor</value>   </property>   <property name="targetField"><value>INSTANCE</value></property> </bean>

You can name this bean anything you like and use the name (exposeInvocation in the preceding example) in the interceptorNames list.

Important 

Try to avoid exposing the MethodInvocation if at all possible. It creates a dependence on the Spring AOP framework, and it's seldom necessary.

Understanding Proxy Types

Spring decouples the implementation of an AOP proxy from the rest of the framework using the AopProxyFactory interface. This defines the following method, to create an AOP proxy given proxy configuration data:

 AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException; 

Proxy configuration objects such as AdvisedSupport control how one or more proxies should be created. Created proxies will normally contain a reference to the configuration object that created them, but this reference can be broken on serialization to allow the proxy to stand alone.

The AopProxyFactory interface enables the Spring AOP API to support proxies implemented with a range of technologies. For example, Spring could use a code generation approach to generate proxy classes, rather than using a reflection-based mechanism. Additional proxy types are currently being considered, but won't change the public API of Spring AOP. The flexibility of the design is shown by the fact that the .NET port of the Spring Framework uses a virtually identical AOP API, despite the very different proxy generation strategies required in a CLI environment.

It's worth understanding the contract of a Spring AOP proxy before we go further:

  • It must implement all interfaces that the configuration indicates should be proxied.

  • It must implement the Advised interface, discussed earlier.

  • It must implement the equals() method to compare proxied interfaces, advisors, and target.

  • It must be serializable if advisors and target are serializable.

  • It must be threadsafe if advisors and target are threadsafe.

Spring 1.1 provides two AopProxy implementations out of the box:

  • JdkDynamicAopProxy: An implementation using a J2SE dynamic proxy. This allows only methods on interfaces to be proxied.

  • Cglib2AopProxy: An implementation using the CGLIB byte code generation library. This enables methods on interfaces or classes to be proxied. CGLIB works by subclassing the target class. Thus you can use CGLIB proxies only if you have a target class. CGLIB cannot override final methods. Spring's CGLIB proxies can implement any number of additional methods besides the methods on the target class.

The type of proxy created is controlled by the AopProxyFactory implementation held in an AdvisedSupport object creating a proxy. The default implementation will use a dynamic proxy where possible (that is, if it hasn't been asked to proxy a target class). There should be no need in normal usage to implement the AopProxyFactory interface; however, Spring, as usual, provides a host of extension points.

You can force the use of CGLIB proxy — even when proxying interface methods only — by setting the proxyTargetClass flag on ProxyConfig (the superclass of all proxy creators) to true.

Important 

If you use CGLIB proxying, the CGLIB JAR file must be on the classpath. Hibernate depends on CGLIB, and some environments, such as the Geronimo application server, use it, so many Spring users already have the required binaries on their classpath. However, users who don't understand the distinction between the two proxy types are often caught out by missing class exceptions relating to CGLIB.

Apart from the fact that CGLIB proxies can proxy methods on classes as well as interfaces, you should notice no difference between the two types of proxies. A few tips for making a choice:

  • If you're still running in a JDK 1.3 environment (for example, because of an old application server version), CGLIB proxies will give much better performance than JDK proxies, which were significantly optimized in 1.4 JVMs. Use CGLIB if the performance overhead of AOP proxying is an issue. It usually isn't, even on JDK 1.3.

  • If you have many methods to which no advice will apply, CGLIB proxies will be significantly faster, as using CGLIB allows greater potential for optimization than dynamic proxies.

  • Besides this special case, you will find very little performance difference between the two types of proxy. CGLIB proxies may be marginally faster overall.

  • If you use only J2SE dynamic proxies, you do not need CGLIB binaries on your classpath, unless they are required by other frameworks. (CGLIB is required to use Spring IoC's Method Injection features, but not otherwise required in Spring itself.)

It's okay to let the framework decide on the proxy type to use in most cases. However, if you can use dynamic proxies, you should probably do so, as they are simpler than CGLIB proxies, and it's a good idea in general to adopt the simplest possible solution.

Debugging and Testing

What are the implications of using Spring AOP for debugging?

Essentially you'll see a few extra lines in the call stack for advised invocations, produced by the Spring AOP infrastructure. Some of these concern the use of reflection to invoke the target object; others result from the AOP framework itself, in which a proxy (in this case JdkDynamicAopProxy) invokes the proceed() method of a MethodInvocation. Each interceptor in turn will invoke proceed() to continue down the interceptor chain:

Thread [main] (Suspended (breakpoint at line 103 in BusinessObject))  BusinessObject.someMethod(String) line: 103    NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]  NativeMethodAccessorImpl.invoke(Object, Object[]) line: not available  DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: not available  Method.invoke(Object, Object[]) line: not available  AopProxyUtils.invokeJoinpointUsingReflection(Object, Method, Object[]) line: 59  ReflectiveMethodInvocation.invokeJoinpoint() line: 151  ReflectiveMethodInvocation.proceed() line: 120  TransactionInterceptor.invoke(MethodInvocation) line: 57  ReflectiveMethodInvocation.proceed() line: 140  JdkDynamicAopProxy.invoke(Object, Method, Object[]) line: 153  $Proxy1.setName(String) line: not available      Caller.callingMethod()

Essentially you can skip through the AOP elements in the stack trace to interpret them.

There's nothing magic about this at runtime. We can set breakpoints in Spring or custom advice, as well as in advised code.

If you are puzzled about the behavior of an AOP proxy, you can cast it to Advised and call the toProxyConfigString() method. The output will look like this example from the Spring test suite:

org.springframework.aop.framework.ProxyFactory:      1 interfaces=[org.springframework.beans.ITestBean];    2 advisors=[{DefaultPointcutAdvisor: pointcut=Pointcut.TRUE; advice=org.springframework.aop.interceptor.NopInterceptor@10ab78a},   {DefaultPointcutAdvisor: pointcut=org.springframework.aop.support.NameMatchMethodPointcut@98062f; advice=org.springframework.aop.framework.CountingBeforeAdvice@be0446}];    targetSource=[SingletonTargetSource: target=(name=null; age=0; touchy=null; spouse={null})];    proxyTargetClass=false; optimize=false; exposeProxy=false; frozen=false;          aopProxyFactory=org.springframework.aop.framework.DefaultAopProxyFactory@10aa282;    advisorChainFactory=org.springframework.aop.framework  .HashMapCachingAdvisorChainFactory@1332109

From the output of toProxyConfigString() you can see the proxied interfaces, the advisors, and their ordering (both pointcut and advice are shown); the TargetSource, which in the case of Singleton TargetSource will call toString() on the target; and a range of AOP configuration parameters. Of course you can also cast to Advised and query programmatically — perhaps even using a scripting language such as Groovy.

Examining AOP configuration and debugging advised objects, however, is really only a matter for integration testing and later stages of testing. In general, you should run unit tests without running the AOP framework.

It's important to remember that with AOP, as with other Spring features, it's possible to run many integration tests outside an application server, even when the code is intended for a J2EE environment. You should normally use the org.springframework.test package, shipped in spring-mock.jar, for this; your Spring AOP configuration will execute just as in production with this approach to integration testing. The PetClinic sample application shipped with Spring illustrates use of this testing package in conjunction with transactional proxying.

Important 

In general, appropriate use of Spring AOP should make debugging easier. AOP serves to keep the framework (and orthogonal concerns) out of your application code; you should reap the rewards.

Understand implications for unit testing. This means that you can — and should — unit test target objects without the AOP framework, as well as perform integration tests.

For example, if we use Spring AOP for transaction management, the contract for application classes is narrowly defined and easily testable. Through the use of rollback rules (discussed in Chapter 6), we need only test that the application classes either succeed or throw the appropriate exceptions to cause rollback in the event of failure. There is no need to start an actual transaction.

Important 

Aspects can decouple application code from its environment. While EJB offered a limited kind of AOP to address a fixed set of concerns such as transaction management and security, it came at the price of an awareness of the code on its environment. With AOP we can avoid this problem. This is beneficial in many scenarios. One of the most important is testing.

Miscellaneous

Let's conclude by covering some miscellaneous questions that come up on the Spring user lists.

Changing Method Arguments

It's possible for an interceptor to change method arguments by modifying the contents of the array returned by the MethodInvocation getArguments() method. Such a change will affect later interceptors in the chain before the target is invoked, the arguments with which the target object is invoked, and any interceptors interested in arguments as the chain returns after the target is invoked.

It is of course trivial for any interceptor to change the return value, by returning a value other than that returned by the MethodInvocation proceed() method.

Double Proxying

It's possible to proxy a proxy — except, at present, when using two CGLIB proxies, because of a limitation of CGLIB that may well have been removed by the time you read this. However, it's best avoided, as it will complicate stack traces and add overhead. There should rarely if ever be any need for double proxying, as it's normally possible to add all required advice to a single proxy. One potentially valid use would be to make the "outer" proxy work with multiple "inner proxy" targets using a TargetSource.

Implementing Multiple AOP Framework Interfaces

A Spring AOP advice should implement only one advice interface. For example, a single class should not implement both MethodBeforeAdvice and AfterReturningAdvice. While it's legal Java (Spring can't prevent application classes implementing whatever interfaces they choose), the effect of deploying such a class within the Spring AOP framework is undefined. Only one of the interfaces will be picked up: There is no guarantee which.

Fortunately, there's a simple and superior alternative: If one class needs to implement more than one advice type, it should just implement MethodInterceptor and be an around advice, giving it total control over the invocation of the join point.

Serializing AOP Proxies

As of Spring 1.1, AOP proxies are serializable if the TargetSource is serializable and all Advisors are serializable. (The latter condition means that both Advice and Pointcut contained in the Advisor must be serializable.) Unless the developer has specified a TargetSource, Spring will automatically use SingletonTargetSource, which is serializable if the target object is serializable.

On deserialization, the proxy will have the same interceptor chain, with all Advisors having been serialized. The deserialized proxy will implement the same interfaces. The TargetSource (including the target) will have been serialized.

Serialization disconnects the AOP proxy from the factory that created it. This means that even if a proxy was created by a ProxyFactoryBean, which depends on its owning BeanFactory, which is probably not serializable, the proxy itself is serializable so long as its advice satisfies these criteria. Disconnection of proxies on serialization is achieved by cloning the basic configuration held in the AdvisedSupport class and its ProxyConfig superclass. State held lower down the inheritance hierarchy than AdvisedSupport (such as ProxyFactoryBean) is ignored, as it relates to how to create more proxies, rather than the supporting data for a working proxy.

Most Spring AOP framework classes — pointcuts, interceptors, and TargetSource implementations — are serializable. Thus, for example:

  • You can serialize an AOP proxy that uses a HotSwappableTargetSource. You can continue to work with the HotSwappableTargetSource on deserialization.

  • You can serialize regular expression pointcuts such as org.springframework.aop.support.Perl5RegExpMethodPointcut, assuming that you have all the necessary regular expression libraries on the client side.

  • You can serialize ControlFlowPointcuts.

  • Transactional AOP proxies are serializable if both the PlatformTransactionManager and TransactionAttributeSource implementation they use are serializable. In practice, this means that transactional proxies are serializable if they use JtaTransactionManager and both client and server environments support access to the JTA UserTransaction.

  • "Prototype based target sources" — subclasses of org.springframework.aop.target.AbstractPrototypeBasedTargetSource such as PrototypeTargetSource and pooling target sources — are serializable, but serialization results in disconnecting a single target object instance from the pool or other dynamic source of target objects. On deserialization, the TargetSource will be a simple SingletonTargetSource wrapping an object obtained from the TargetSource before serialization.

The AOP proxy serialization infrastructure is designed to minimize the amount of state that has to be carried across the wire. However, advised objects will be more expensive to serialize than plain Java objects, and there may also be some cost to rebuild state on deserialization. As usual, avoid remoting if possible.

There is no guarantee that the serialized form of AOP proxies will be compatible across different versions of Spring.

The onus is on the application developer to choose whether an interceptor or other AOP framework class is serializable. If so, it should be marked as Serializable. Framework interfaces such as Pointcut and MethodInterceptor do not themselves extend java.io.Serializable. However, framework classes such as DefaultPointcutAdvisor, which are designed for use without subclassing, do implement Serializable, so they do not prevent serializable application classes being serialized.

Invoking proceed() More Than Once

What if you want to retry invocation of the relevant method on the target object — for example, if you want to retry in the event of failure, or if you simply want to invoke the method more than once?

It's not possible for an interceptor to call proceed() more than once on its MethodInvocation argument, because invoking proceed() will exhaust the interceptor chain held in the MethodInvocation. A MethodInvocation holds a list of interceptors and maintains the position of the current interceptor; once all interceptors have been run, it is illogical to call the proceed() method.

However, multiple calls on the rest of the interceptor chain can be achieved using the invocableClone() method of org.springframework.aop.framework.ReflectiveMethodInvocation, which was added for this reason. It's possible to cast the MethodInvocation argument to the MethodInterceptor.invoke() method to ReflectiveMethodInvocation whenever using Spring's AOP framework, as the MethodInvocation instance will be of class ReflectiveMethodInvocation or a subclass. You can call proceed() on each copy (including the original object), as shown in this example:

MethodInterceptor doubleInvoker = new MethodInterceptor() { public Object invoke(MethodInvocation mi) throws Throwable {  // Clone the invocation to proceed twice  MethodInvocation clone = ((ReflectiveMethodInvocation) mi).invocableClone();  clone.proceed();  return mi.proceed(); }

There are some implications to consider here. If using a custom TargetSource, the target will be obtained when the original MethodInvocation is created. The copy will use the same TargetSource. So, for example, if the target comes from a pool, all calls to proceed() on MethodInvocation clones will hit the same target instance.

If you've chosen to expose the MethodInvocation using a ThreadLocal, and the target relies on it, the target will see the original method invocation in all cases, not the clones. However, this shouldn't normally matter, and, as we've seen, exposing the MethodInvocation is best avoided.

The invocableClone() method does a shallow copy of the invocation except for the arguments array. The arguments array is deep copied, meaning that it's possible to change the arguments independently when making multiple invocations.



Professional Java Development with the Spring Framework
Professional Java Development with the Spring Framework
ISBN: 0764574833
EAN: 2147483647
Year: 2003
Pages: 188

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net