AOP Proxies in Spring

Aspect-Oriented Programming(AOP) complements Object-Oriented Programming(OOP) by providing another way of thinking about program structure. In addition to classes, AOP gives you
aspects. Aspects enable modularization of concerns such as transaction management that cut across multiple types and objects. (Such concernsare often termed crosscutting concerns.)

One of the key components of Spring is the AOP framework. While the Spring IoC container does not depend on AOP, meaning you don't need to use AOP if you don't want to, AOP complements Spring IoC to provide a very capable middle-ware solution.

AOP is used in the Spring Framework:


  • To provide declarative enterprise services, especially as a replacement for EJB declarative services. The most important such service is declarative transaction management, which builds on the Spring Framework's transaction abstraction.

  • To allow users to implement custom aspects, complementing their use of OOP with AOP

Proxying mechanisms

Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. (JDK dynamic proxies are preferred whenever you have a choice).
If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. All of the interfaces implemented by the target type will be proxied. If the target object does not implement any interfaces then a CGLIB proxy will be created.
If you want to force the use of CGLIB proxying (for example, to proxy every method defined for the target object, not just those implemented by its interfaces) you can do so. However, there are some issues to consider:
  • final methods cannot be advised, as they cannot be overriden.
  • You will need the CGLIB 2 binaries on your classpath, whereas dynamic proxies are available with the JDK. Spring will automatically warn you when it needs CGLIB and the CGLIB library classes are not found on the classpath.
  • The constructor of your proxied object will be called twice. This is a natural consequence of the CGLIB proxy model whereby a subclass is generated for each proxied object. For each proxied instance, two objects are created: the actual proxied object and an instance of the subclass that implements the advice. This behavior is not exhibited when using JDK proxies. Usually, calling the constructor of the proxied type twice, is not an issue, as there are usually only assignments taking place and no real logic is implemented in the constructor.
To force the use of CGLIB proxies set the value of the proxy-target-class attribute of the <aop:config> element to true:
<aop:config proxy-target-class="true">
    <!-- other beans defined here... -->
</aop:config>
To force CGLIB proxying when using the @AspectJ autoproxy support, set the 'proxy-target-class' attribute of the element to true:

<aop:aspectj-autoproxy proxy-target-class="true">
  <!-- other beans defined here... -->
</aop:aspectj-autoproxy>
CGLIB: Byte Code Generation Library is high level API to generate and transform JAVA byte code. It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access.
CGLIB is required to process @Configuration classes.
Hibernate uses cglib to transparently implement Proxy behavior in classes that need to be persistent at run time.

 Understanding AOP proxies

Spring AOP is proxy-based. It is vitally important that you grasp the semantics of what that last statement actually means before you write your own aspects or use any of the Spring AOP-based aspects supplied with the Spring Framework.

Consider first the scenario where you have a plain-vanilla, un-proxied, nothing-special-about-it, straight object reference, as illustrated by the following codesnippet.

public class SimplePojo implements Pojo {

   public void foo() {
      // this next method invocation is a direct call on the 'this' reference
      this.bar();
   }
   
   public void bar() {
      // some logic...
   }
}
If you invoke a method on an object reference, the method is invoked directly on that object reference, as can be seen below.

AOP Proxies in Spring

public class Main {

   public static void main(String[] args) {
   
      Pojo pojo = new SimplePojo();
      
      // this is a direct method call on the 'pojo' reference
      pojo.foo();
   }
}

Things change slightly when the reference that client code has is a proxy. Consider the following diagram and code snippet.
AOP Proxies

public class Main {

   public static void main(String[] args) {
   
      ProxyFactory factory = new ProxyFactory(new SimplePojo());
      factory.addInterface(Pojo.class);
      factory.addAdvice(new RetryAdvice());

      Pojo pojo = (Pojo) factory.getProxy();
      
      // this is a method call on the proxy!
      pojo.foo();
   }
}

The key thing to understand here is that the client code inside the main(..) of the Main class has a reference to the proxy. This means that method calls on that object reference will be calls on the proxy, and as such the proxy will be able to delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object, the SimplePojo reference in this case, any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy. This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.

Okay, so what is to be done about this? The best approach (the term best is used loosely here) is to refactor your code such that the self-invocation does not happen. For sure, this does entail some work on your part, but it is the best, least-invasive approach. The next approach is absolutely horrendous, and I am almost reticent to point it out precisely because it is so horrendous. You can (choke!) totally tie the logic within your class to Spring AOP by doing this:

public class SimplePojo implements Pojo {

   public void foo() {
      // this works, but... gah!
      ((Pojo) AopContext.currentProxy()).bar();
   }
   
   public void bar() {
      // some logic...
   }
}

This totally couples your code to Spring AOP, and it makes the class itself aware of the fact that it is being used in an AOP context, which flies in the face of AOP. It also requires some additional configuration when the proxy is being created:

public class Main {

   public static void main(String[] args) {
   
      ProxyFactory factory = new ProxyFactory(new SimplePojo());
      factory.adddInterface(Pojo.class);
      factory.addAdvice(new RetryAdvice());
      factory.setExposeProxy(true);

      Pojo pojo = (Pojo) factory.getProxy();

      // this is a method call on the proxy!
      pojo.foo();
   }
}

Finally, it must be noted that AspectJ does not have this self-invocation issue because it is not a proxy-based AOP framework.

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

AOP Proxy

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

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.

Spring AOP

In Spring AOP, it is not possible to have aspects themselves be the target of advice from other aspects.

Spring AOP Proxies





No comments:

Post a Comment