问题
最近在完成一个需求的时候,发现原本配置好的拦截器有时能生效有时又不生效,很奇怪。如果拦截器生效了,那基本可以排除是拦截器本身的问题。
那具体的问题我看了代码之后,也不太清楚,方法确实被调用了,断点调试当中就是没有进入拦截器。
分析
经过排查,我发现这个问题主要是出在调用方式上。
众所周知,AOP是通过代理对象(通常是通过Spring容器管理的)实现的。Spring使用动态代理来在方法调用时插入额外的行为(如日志记录、事务管理等)。当你通过Spring容器调用某个类的方法时,如果该方法被AOP配置了拦截规则(比如我的拦截器, @TraceConfig
注解),Spring会在代理对象上拦截该方法,执行额外的逻辑。
当你在类的内部调用自身的方法时,比如使用 this.method()
方式调用,实际上调用的是该类的原始对象的方法,而不是代理对象的方法。因为AOP代理的前提是通过Spring容器管理的代理对象调用方法,而 this.method()
是直接调用当前类的实例(原始对象)中的方法,不会通过Spring的AOP代理,因此不会触发AOP的拦截器。
当你通过Spring容器调用一个类的方法时,Spring会给这个类生成一个代理对象。这个代理对象负责在方法执行前后插入额外的逻辑。但是,当类内部通过 this
调用方法时,不会经过代理对象,而是直接调用当前实例的原始方法,所以AOP拦截不会生效。
@Component
public class ExampleClass {
// AOP不会生效
public void method1() {
this.method2(); // 不会触发拦截器
}
@TraceConfig(configPath = "example")
public void method2() {
// 被拦截器拦截
}
}
解决方法
经过上面的解释,解决方法已经呼之欲出了,那就是保证方法调用来自外部,通过代理对象进行调用。
1.启用exposeProxy
spring:
aop:
proxy-target-class: true
expose-proxy: true
通过这种设置,Spring 会暴露当前的代理对象,使得可以通过 AopContext.currentProxy()
获取。
2.确保在同一线程中调用: 只有在 AOP 代理对象的上下文中,AopContext.currentProxy()
才能被正确调用。如果你在类内部通过 this.method()
调用时,AOP
拦截是不会生效的,因为 this
直接引用的是原始对象,而不是代理对象。
3.正确的调用方式: 通过调用 AopContext.currentProxy()
来获得当前代理对象,然后调用代理对象的方法,而不是直接通过 this
调用。
// 错误调用:this.method() 不会触发 AOP
this.someMethod();
// 正确调用:通过代理对象调用方法
((YourClass) AopContext.currentProxy()).someMethod();
代码示例
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@TraceConfig
public void methodA() {
System.out.println("Executing methodA");
// 调用代理对象的methodB,而不是直接使用this.methodB()
((MyService) AopContext.currentProxy()).methodB();
}
@TraceConfig
public void methodB() {
System.out.println("Executing methodB");
}
}
总结
- 确保
exposeProxy
属性在 Spring 配置中设置为true
。 - 使用
AopContext.currentProxy()
获取代理对象,避免直接通过this
调用方法。 - 确保在同一线程中调用代理对象,否则
AopContext.currentProxy()
将无法正常工作。