I'm going to talk about 'self-calls' here, so if you have an experience with that feel free to skip the further material.
Please note that the spring reference describes the same problem - 8.6.1 Understanding AOP proxies. However, I found a lot of posts at the spring forums showing that people are unaware about it. So, I tried to explain it a bit wider/using another words.
Lets create a test-case that illustrates the problem. Suppose we're new to spring and just created a cool code that uses spring aop and works fine:
So, we see that there is a simple service class that just prints to stdout that its methods are called and simple aspect that intercepts all public method calls of that service and prints information about interception to the stdout as well. If we run SpringStart class we see the expected output - target service methods are intercepted by the aspect:
Lets expand our service class a little:
We expect that the aspect is executed three times - service() and anotherService() calls from SpringStart.main() and service() call from AopService.anotherService(). However, if we run the example we can see that the aspect is executed only two times (for the calls issued from SpringStart.main()), i.e. service() call from AopService.anotherService() is not intercepted:
As usual, problem root is tightly connected to The Law of Leaky Abstractions. I.e. we had particular expectations about aspect behavior but didn't realize that spring aop principles doesn't allow the expectations to occur.
The general idea is that spring AOP is proxy-based, i.e. it assumes that when the bean is used as a dependency and its method(s) should be advised by particular aspect(s) the container injects aspect-aware bean proxy instead of the bean itself. For example, returned proxy uses approximately the same algorithm as the one below:
I.e. the proxy is aspect-aware and it calls necessary aspect method before delegating the call to the 'raw' bean. However, last AopService implementation used to call service() from anotherService() - there is just no chance for the proxy to intercept that because the call is performed against 'this' reference, i.e. the call is performed on the 'rawBean' itself. That is the exact answer to why the aspect was not applied at that situation.
Now, when we understand the problem root we can talk about the ways to solve it. There are at least three ways:
- rewrite the code in order to avoid self-calls that should be advised by the aspects - rather inconvenient, especially if you work with the legacy code;
- replace self-calls with aspect-aware proxy calls, i.e. use the statement like ((AopService) AopContext.currentProxy()).service() instead of service() at the anotherService() - rather inconvenient too, requires additional proxy setup and totally couples the code to the spring;
- use aspectj weaving - that is my personal choice. Its idea is to inject aspect logic directly to the target class, i.e. remove the necessity to have all methods being proxied;
I'm going to create a small article that shows how to use various types of aspectj weaving with spring and that such approach completely eliminates the problem.