Secrets of the Spring AOP Proxy

Spring Aspect Oriented Programming (AOP) is a powerful mechanism to weave cross cutting concerns like security, transactions, exception handling, logging, etc. into business code (“core concerns”) without explicitly adding calls to the cross cutting concern.  This allows the cross cutting concern to be updated, removed, swapped out, and generally maintained in a much simpler fashion.

AOP UNDER THE COVERS – THE PROXY

Having said that, because of the way AOP is implemented, there can seem to be a certain amount of mystery and magic at work.  Your code never invokes the advice methods of an aspect class.  Spring does it for you based on your XML configuration or annotations on the aspect class.  How does the magic happen?  Under the covers, Spring implements a proxy to intercept calls to a target object.

As an example, say you have a service bean that invokes a call to the saveCustomer( ) method on a DAO.

image

Now say you want to have some logging (a cross cutting concern) occur when a call to any save method occurs on a DAO.  Spring detects your need to call on a logging aspect through your AOP configuration or annotations.  When it does, it builds a proxy (called CustomerDaoProxy for example sake here) around the “target” object – in this case the DAO.

image

Now, on a call to a save method in the DAO, the proxy intercepts the call and routes it appropriately to the appropriate advice method in the aspect class.

image

ISSUES GIVEN PROXY-BASED AOP

This proxy-based mechanism allows powerful cross cutting concern code to be isolated from business code.  However, there are all kinds of “gotcha’s” that can occur with this type of under-the-covers implementation of which you should be aware.  First of all, aspects cannot advise other aspects.  Per the Spring documentation:

Advising aspects with other aspects?

In Spring AOP, it is not possible to have aspects themselves be the target of advice from other aspects. The @Aspect annotation on a class marks it as an aspect, and hence excludes it from auto-proxying.  (See:  static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-at-aspectj).

This can have some big implications in, sometimes even limitations on, how you implement a cross cutting concern.  Say, for example, your form of logging required adding log statements to a database.  Further, say this database entry required to be performed as part of a transaction (probably a reasonable assumption).  If transactions were implemented through aspects, then you would have the log aspect a target of advice from a transaction aspect.  That’s a big “NGH” – not gonna happen.

image

Another such “gotcha” is that local or internal calls to methods within a target object do not cause the advice method to be invoked even with both methods are captured by the AOP pointcut.  In other words, a call from a method in a target to another method in the target does not get intercepted by the proxy.  This results in the advice method never being triggered.  For example, assume you had some sort of business bean that made a call to itself as shown in the simple example below.

   1: @Component("mySubordinate")
   2: public class CoreBusinessSubordinate {
   3:
   4:     public void doSomethingBig() {
   5:         System.out.println("I did something small");
   6:     }
   7:
   8:     public void doSomethingSmall(int x){
   9:         System.out.println("I also do something small but with an int");
  10:     }
  11: }

Also assume you had an aspect constructed and configured with a pointcut capturing both CoreBusinessSubordinate methods as the CrossCuttingConcern class shown here.

   1: @Component
   2: @Aspect
   3: public class CrossCuttingConcern {
   4:
   5:     @Before("execution(* com.intertech.CoreBusinessSubordinate.*(..))")
   6:     public void doCrossCutStuff(){
   7:         System.out.println("Doing the cross cutting concern now");
   8:     }
   9: }

If another bean was to explicitly call both the target doSomethingBig( ) and doSomethingSmall( ) methods directly, the proxy/interceptor of the aspect would cause the advice to fire twice – as expected.

   1: @Service
   2: public class CoreBusinessKickOff {
   3:
   4:     @Autowired
   5:     CoreBusinessSubordinate subordinate;
   6:
   7:     // getter/setters
   8:
   9:     public void kickOff() {
  10:         System.out.println("I do something big");
  11:         subordinate.doSomethingBig();
  12:         subordinate.doSomethingSmall(4);
  13:     }
  14: }

Results of calling kickOff above given code above.

I do something big

Doing the cross cutting concern now

I did something small

Doing the cross cutting concern now

I also do something small but with an int

However, if instead of calling the doSomethingSmall( ) method from another bean, it was called from inside the target as shown in this next example, the advice fires only one time!

   1: @Component("mySubordinate")
   2: public class CoreBusinessSubordinate {
   3:
   4:     public void doSomethingBig() {
   5:         System.out.println("I did something small");
   6:         doSomethingSmall(4);
   7:     }
   8:
   9:     public void doSomethingSmall(int x){
  10:         System.out.println("I also do something small but with an int");
  11:     }
  12: }
  13:
  14: --------------------------------------------
  15:
  16: public void kickOff() {
  17:     System.out.println("I do something big");
  18:     subordinate.doSomethingBig();
  19:     //subordinate.doSomethingSmall(4);
  20: }

New results of calling kickOff above.

I do something big

Doing the cross cutting concern now

I did something small

I also do something small but with an int

The internal call from the target method to another target method escapes the aspect interceptor.

WORK AROUND

You can sometimes find a work around for AOP proxy issues.  For example, you can work around this last issue by exposing the proxy to the target.  When configuring autoproxy development in your configuration, add an expose-proxy attribute and set its value to true (by default it is false).

   1: <aop:aspectj-autoproxy expose-proxy="true"/>

Now in your target code, you can reroute self-invocating methods back through the target proxy.  Use AopContext.currentProxy( ) to get the proxy object and then invoke the internal target method through the proxy as shown below.

   1: public void doSomethingBig() {
   2:     System.out.println("I did something small");
   3:     //doSomethingSmall(4);
   4:     ((CoreBusinessSubordinate) AopContext.currentProxy()).doSomethingSmall(4);
   5: }

Of course, take heed that this tightly couples your core business concern to AOP – something you are trying to avoid with AOP to begin with.  So it may be better to refactor the code such that internal calls are not required when you need advice to fire on all methods.
FROM HERE

Advertisements

One comment

  1. sauribabu · June 6

    Reblogged this on sauribabu.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s