Spring AOP切点未触发:内部调用@Track注解方法无响应
兄弟,你的切面配置其实没毛病——Autowired对象的@Track方法能正常被拦截,就证明切点定义、切面逻辑都是有效的。问题出在Spring AOP的动态代理机制上:
当你在Engine类的processTask里直接调用自身的someMethodOfEngineClass时,这个调用是目标对象内部的直接方法调用,完全没经过Spring为Engine生成的代理对象。而Spring AOP的切面逻辑是织入在代理对象中的,这种自调用自然触发不了切点。
至于切面放在子包的问题:只要你的切面类加了@Component,且Spring的组件扫描路径覆盖到了这个子包(比如主启动类的@ComponentScan包含子包,或者用了默认的扫描规则),就一点影响都没有,这不是问题的诱因。
这里给你几个实用的解决办法,按推荐程度排序:
1. 注入自身代理对象调用
在Engine类里自己注入自身的代理实例,然后通过这个实例调用目标方法(注意要把私有方法改成public/protected/default,因为代理无法访问私有方法):
@Component public class Engine { @Autowired private Engine self; // 注入自身的代理对象 public void processTask() { // 不要直接调用someMethodOfEngineClass(),而是用self调用 self.someMethodOfEngineClass(); } @Track public void someMethodOfEngineClass() { // 业务逻辑 } }
2. 把自调用方法抽成独立Bean
把someMethodOfEngineClass的逻辑抽成一个单独的Spring Bean,然后在Engine里注入这个Bean来调用——这种方式最符合Spring的依赖注入思想,代码解耦也更清晰,优先推荐:
// 新建一个独立的Bean @Component public class EngineHelper { @Track public void someMethod() { // 原来的业务逻辑 } } // 在Engine里注入调用 @Component public class Engine { @Autowired private EngineHelper engineHelper; public void processTask() { engineHelper.someMethod(); } }
3. 使用AspectJ静态织入
如果不想修改代码结构,可以改用AspectJ的编译时或加载时织入——它是直接修改字节码,不受动态代理的自调用限制:
- 编译时织入:需要在maven/gradle里添加AspectJ插件,编译时就把切面逻辑织入到目标类字节码中。
- 加载时织入:需要添加VM参数
-javaagent:aspectjweaver.jar,在类加载时织入切面。
这种方式配置稍复杂,但不需要修改业务代码,适合原有代码结构不好调整的场景。
4. 从ApplicationContext获取代理对象
在Engine类中注入ApplicationContext,然后从中获取自身的代理对象,原理和第一种方法类似:
@Component public class Engine implements ApplicationContextAware { private Engine self; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.self = applicationContext.getBean(Engine.class); } public void processTask() { self.someMethodOfEngineClass(); } @Track public void someMethodOfEngineClass() { // 业务逻辑 } }
内容的提问来源于stack exchange,提问作者Partha

