The problem with proxy-based AOP frameworks

Proxy-based AOP frameworks like the one in the Spring Framework have a not oft discussed problem. When a method in a class invokes another method in the same class (or a superclass), any interceptors on that second method are not executed. This unexpected behaviour can have serious consequences when the interceptor delivers important services like transactions or security.

Let's say we are implementing an application for a bank where only certain employees are allowed to perform a credit check on suspect transaction, e.g. those exceeding 1 million euros. Using Aspect Oriented Programming this can be implemented as follows:

When processed by AspectJ's precompiler, the following woven class is output:

As you can see in the picture, when the method processPayment is invoked, the correct security check is performed when the checkCreditRating method is invoked.

However, that precompilation step was perceived as complicated and hard to integrate into the build proces (and not every IDE has appropiate plugins). This led to the appearance of proxy-based AOP solutions such as dynaop and Spring's AOP framework. A precompilation step is no longer necessary. Instead, a proxy is created on the fly. This proxy performs the functionality defined in the aspect before (and after) invoking the method in the original object:

Apart from only offering method interception instead of the full range of AOP functionality, this approach has one other, not often discussed, drawback. When invoking the checkCreditRating method on the proxy, the security check is correctly performed. However, when invoking the processPayment method on the proxy (see the picture), control is passed to the processPayment method in the original object which invokes this.checkCreditRating() directly, thereby bypassing the security check. This occurs because this in the original object refers to that object itself instead of the proxy.

Other functionality that assumes the identity of the proxy and the identity of the original object are identical will fail too. As mentioned in GoF on page 178 when discussing the Decorator pattern:

3. A decorator and its component aren't identical. A decorator acts as a transparent enclosure. But from an object identity point of view, a decorated component is not identical to the component itself. Hence you shouldn't rely on object identity when you use decorators.

This is an unexpected result of the proxy-based implementation of AOP and one that you need to be especially aware of when implementing important functionality in a aspect!

Comments (8)

  1. Alef Arendsen - Reply

    August 22, 2006 at 2:27 am

    Hi Vincent,

    yes, provy-based AOP solution do have this specific drawback, although I haven't seen it become a real bottleneck in any of the projects I've encountered. Good to spread the word though, as knowing about it is a good first step.

    Your security use case is one situation where the problem might occur, another is different propagation levels on method within the same class.

    class MyClass {

    // propagation=REQUIRES
    void doIt() {
    this.doIt2();
    }

    // propagation=REQUIRES_NEW
    void doIt2() {
    }
    }

    Note that Load-Time-Weaving (LTW) has solved lots of the deploytime configuration problems that AspectJ used to come with. Using LTW does involve some additional configuration, but you don't necessarily have to issue a post compilation step anymore. This works nicely together with the new Java 5 agents.

    cheers,
    Alef

  2. Vincent Partington - Reply

    August 23, 2006 at 2:15 pm

    Hi Alef,

    I expect a lot from the LTW features of AspectJ 5. Not only will it solve this deficiency of the proxy-based AOP solution, it will also allow developers to use the more exotic AOP features such as introductions.

    Regards, Vincent.

  3. Erwin Bolwidt - Reply

    August 23, 2006 at 5:39 pm

    Hi Vincent & Alef,

    In applications where one comes across this issue occasionally, and one is not ready to use LTW, one can use the AopContext solution.

    On any ProxyFactoryBean you can set the 'exposeProxy' property to 'true' (it is false by default, for performance reasons, but the performance impact is very small.)

    This property cooperates with the class org.springframework.aop.framework.AopContext, in such a way that it makes the AOP proxy available to the proxied instance.

    Sounds complicated?

    An example:

    Say your BankService's processPayment method needs to call checkCreditRating, but needs all security inerceptors to be invoked.

    Instead of calling this.checkCreditRating(), it calls

    BankService proxy = (BankService) AopContext.getCurrentProxy();
    proxy.checkCreditRating();

    It won't receive any awards for beautiful code, but if not too frequently used, it is a good workaround.

    I've used it in practice with one addition: to make unit testing easier; adding a fallback to an injectable property in the case that AopContext didn't return a value. (Which occurs when running the code without an interceptor, in a unit test)

    Regards,
    Erwin

  4. Alex Popescu - Reply

    September 16, 2006 at 1:30 am

    The entry is quite interesting, but it needs a small correction: AspectWerkz is/was not a proxy-based AOP solution. It offered an additional functionality to work with proxies, but the core was a weaving based AOP solution (it performed the weaving with the help of classloaders).

    ./alex
    --
    .w( the_mindstorm )p.
    AspectWerkz developer

  5. Vincent Partington - Reply

    September 20, 2006 at 11:51 am

    Oops. my bad, Alex. I've fixed the article.

    Regards, Vincent.

  6. Patrick Vanbrabant - Reply

    October 4, 2006 at 1:16 am

    Erwin,

    Regarding the AopContext solution, I think this is not a real solution anyway.

    In this case the class itself must know that there is an advise applied to some of its methods. In the case of the BankAccount this may seam trivial, but lets look at an even more down to earth example.

    The textbook case for applying Aop would be logging or monitoring. Lets assume that we have a simple logging advice that we want to apply on each method of the interface.

    Then the developer of each class must know whether this class will always have the advide applied to it. Sounds a little like mixing of concerns.

    If we then want to turn of (and on again) the logging in a production environment we have to change all the code again. Like I saym not a real solution.

    Just my five cents.

    KR

    Patrick

  7. [...] After my initial explorations of the AspectJ-support offered by Spring, someone (actually, I think it was google again) pointed me to this article about the drawbacks of proxied aop. A good read written by Vincent Partington that shows the internals of  how and why a proxy based aop framework can ignore your carefully injected aspects. A ‘must-be-aware-of’ for every Spring 2.0 user! [...]

  8. Vincent Partington - Reply

    October 28, 2006 at 10:46 am

    Hi Okke from LogicaCMG,

    (Note: I tried replying to your blog but my remark did not appear there)

    Nice idea to use AOP to address a problem in AOP. 🙂

    Two remarks though:
    - AspectJ is _not_ a proxy-based AOP solution so you would not have the problem there. It's only when using Spring 1.x's proxy-based AOP stuff that you'd have to aware of the problem I described.
    - You can use inter-type declarations to introduce the member "proxy" in all @Proxied classes. See
    http://www.eclipse.org/aspectj/doc/released/progguide/starting-aspectj.html#inter-type-declarations

    Regards, Vincent.

Add a Comment