I understand that Spring AOP is very limited in its abilities (it can only cut into public methods of classes that are Spring beans, and only when those methods are called from outside the class). But now I've discovered another baffling limitation when interfaces are involved.
Normally, if a class is subclassed, Spring AOP has no problem cutting into all of their methods (even overridden ones):
public class A {
public void methodA() { } //OK, can cut in
}
public class B extends A {
@Override
public void methodA() { } //OK, can cut in
public void methodB() { } //OK, can cut in
}
But when we add an interface into the mix, things get pretty bad for Spring AOP:
public interface I {
public void methodA();
}
public class A implements I {
@Override
public void methodA() { } //OK, can cut in
public void methodB() { } //Fail, cannot see or cut into this method
}
public class B extends A {
@Override
public void methodA() { } //Fail, cannot see or cut into this method
public void methodC() { } //Fail, cannot see or cut into this method
}
First of all, Spring AOP can only cut into methods that are in the interface, anything else - it cannot see. Second, it can only cut into the method that directly implements the interface's method - A.methodA()
. It cannot cut into the same method overridden by B.
I am using a generic pointcut expression "execution(* method*(..))"
to cut into all possible methods, so it's not an expression issue.
Is there any way around this limitation? Or should I just forget about Spring AOP and use a different approach?
UPDATE: Ok, I have found the real cause of the problem. I was actually relying on Intellij IDEA's AOP plugin to test this. It's supposed to link the pointcut to all affected methods. But it was using the 'old', dynamic JDK proxy strategy instead of the new, CGLIB strategy. So it wasn't linking it to all methods, but when I actually ran my program, it would cut into all methods correctly.
I'm using Spring Boot 2, which uses the 'new' CGLIB strategy. But on SB1 it might still use the 'old' dynamic JDK proxy strategy, so it might still not work there.
Spring will use either dynamic proxy or cglib to implement AOP.
Cglib is picked if there is no interface, then it will effectively create a subclass of the target class, and override all methods in the target class. With this way all methods could be cut in, except final and static ones.
In case the target class is with interface, then Spring might use a dynamic proxy using one of the interface, and apprantly this will only affect the methods declared in the interface.
Before spring-boot 2.0, dynamic proxy is the default strategy. Now Cglib is the default strategy after spring-boot 2.0.
It seems to me spring probably take the dynamic proxy approach in your case. You could add spring.aop.proxy-target-class: true in your application.yaml to force use Cglib.
In case you still have issue, it's better to post more complete code snippet showing how the mothods are invoked.