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!