我了解到Spring AOP的能力非常有限(它只能切入Spring Bean类的公共方法,并且只能从类外部调用这些方法)。但是现在我发现了涉及接口时的另一个莫名其妙的局限性。
通常,如果一个类是子类,则Spring AOP切入其所有方法(即使是被覆盖的方法)也没有问题:
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
}
但是,当我们在混合中添加接口时,对于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
}
首先,Spring AOP只能切入接口中的方法,其他任何方法都无法看到。其次,它只能切入直接实现接口方法的方法-A.methodA()
。它不能切入被B覆盖的相同方法。
我正在使用通用切入点表达式"execution(* method*(..))"
来切入所有可能的方法,因此这不是表达式问题。
有什么办法可以解决这个限制?还是我应该忘记Spring AOP并使用其他方法?
更新: 好的,我已经找到了问题的真正原因。我实际上是依靠Intellij IDEA的AOP插件来测试的。应该将切入点链接到所有受影响的方法。但是它使用的是“旧的”动态JDK代理策略,而不是新的CGLIB策略。因此,它并不是将其链接到所有方法,但是当我实际运行程序时,它将正确地切入所有方法。
我正在使用Spring Boot 2,它使用了“新” CGLIB策略。但是在SB1上,它可能仍然使用“旧的”动态JDK代理策略,因此它可能在那里仍然不起作用。
答案 0 :(得分:5)
Spring将使用动态代理或cglib来实现AOP。
如果没有接口,则选择Cglib,它将有效地创建目标类的子类,并覆盖目标类中的所有方法。通过这种方式,除了最终方法和静态方法之外,所有方法都可以切入。
如果目标类具有接口,则Spring可以使用其中一个接口使用动态代理,并且显然这只会影响接口中声明的方法。
在spring-boot 2.0之前,动态代理是默认策略。现在,Cglib是spring-boot 2.0之后的默认策略。
在我看来,您的情况似乎可以采用动态代理方法。您可以在application.yaml中添加 spring.aop.proxy-target-class:true 来强制使用Cglib。
如果仍然有问题,最好发布更完整的代码片段以显示如何调用方法。