SpringBoot:同⼀个类中调⽤另⼀个⽅法没有触发SpringAOP
在同⼀个类中调⽤另⼀个⽅法没有触发 Spring AOP 的问题
起因
考虑如下⼀个例⼦:
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented public @interface MyMonitor { }
@Component
@Aspect
public class MyAopAdviseDefine { private Logger logger = Logger(getClass()); @Pointcut("@s.demo4.MyMonitor)")
@Service
public class SomeService { private Logger logger = Logger(getClass()); public void hello(String someParam) { logger.info("---SomeService: he
@EnableAspectJAutoProxy(proxyTargetClass = true)
@SpringBootAppliMyion
public class MyAopDemo { @Autowired SomeService someService; public static void main(String[] args) { SpringAppliMyion.run(MyAopDemo.class, args); }
在这个例⼦中, 我们定义了⼀个注解 MyMonitor, 这个是⼀个⽅法注解, 我们的期望是当有此注解的⽅法被调⽤时, 需要执⾏指定的切⾯逻辑,
即执⾏ MyAopAdviseDefine.logMethodInvokeParam ⽅法.
在 SomeService 类中, ⽅法 test() 被 MyMonitor 所注解, 因此调⽤ test() ⽅法时, 应该会触发 logMethodInvokeParam ⽅法的调⽤. 不
过有⼀点我们需要注意到, 我们在 MyAopDemo 测试例⼦中, 并没有直接调⽤ st() ⽅法, ⽽是调⽤了
SomeService.hello() ⽅法, 在 hello ⽅法中, 调⽤了同⼀个类内部的 st() ⽅法. 按理说, test() ⽅法被调⽤时, 会触发 AOP
逻辑, 但是在这个例⼦中, 我们并没有如愿地看到 MyAopAdviseDefine.logMethodInvokeParam ⽅法的调⽤, 这是为什么呢?
这是由于 Spring AOP (包括动态代理和 CGLIB 的 AOP) 的限制导致的. Spring AOP 并不是扩展了⼀个类(⽬标对象), ⽽是使⽤了⼀个代
理对象来包装⽬标对象, 并拦截⽬标对象的⽅法调⽤. 这样的实现带来的影响是: 在⽬标对象中调⽤⾃⼰类内部实现的⽅法时, 这些调⽤并不会
转发到代理对象中, 甚⾄代理对象都不知道有此调⽤的存在.
即考虑到上⾯的代码中, 我们在 MyAopDemo.aopTest() 中, 调⽤了 someService.hello("abc"), 这⾥的 someService bean 其实是
Spring AOP 所⾃动实例化的⼀个代理对象, 当调⽤ hello() ⽅法时, 先进⼊到此代理对象的同名⽅法中, 然后在代理对象中执⾏ AOP 逻辑
springboot aop
(因为 hello ⽅法并没有注⼊ AOP 横切逻辑, 因此调⽤它不会有额外的事情发⽣), 当代理对象中执⾏完毕横切逻辑后, 才将调⽤请求转发到⽬
标对象的 hello() ⽅法上. 因此当代码执⾏到 hello() ⽅法内部时, 此时的 this 其实就不是代理对象了, ⽽是⽬标对象, 因此再调⽤
简单来说, 在 MyAopDemo 中所看到的 someService 这个 bean 和在 SomeService.hello() ⽅法内部上下⽂中的 this 其实代表的不是同
⼀个对象(可以通过分别打印两者的 hashCode 以验证), 前者是 Spring AOP 所⽣成的代理对象, ⽽后者才是真正的⽬标对象
(SomeService 实例).
解决
弄懂了上⾯的分析, 那么解决这个问题就⼗分简单了. 既然 test() ⽅法调⽤没有触发 AOP 逻辑的原因是因为我们以⽬标对象的⾝份(target
object) 来调⽤的, 那么解决的关键⾃然就是以代理对象(proxied object)的⾝份来调⽤ test() ⽅法.因此针对于上⾯的例⼦, 我们进⾏如下修
改即可:
@Service
public class SomeService { private Logger logger = Logger(getClass()); @Autowired private SomeService self; public void hello(String somePa
上⾯展⽰的代码中, 我们使⽤了⼀种很 subtle 的⽅式, 即将 SomeService bean 注⼊到 self 字段中(这⾥再次强调的是, SomeService
bean 实际上是⼀个代理对象, 它和 this 引⽤所指向的对象并不是同⼀个对象), 因此我们在 hello ⽅法调⽤中, 使⽤ st() 的⽅式来调⽤
test() ⽅法, 这样就会触发 AOP 逻辑了.
Spring AOP 导致的 @Transactional 不⽣效的问题
这个问题同样地会影响到 @Transactional 注解的使⽤, 因为 @Transactional 注解本质上也是由 AOP 所实现的.
例如我在 stackoverflow 上看到的⼀个类似的问题: 这⾥也记录下来以作参考.
那个哥们遇到的问题如下:
public class UserService { @Transactional public boolean addUser(String userName, String password) { try { // call DAO layer and adds to database. } catch (Th
他在 addUser ⽅法上使⽤ @Transactional 来使⽤事务功能, 然后他在外部服务中, 通过调⽤ addUsers ⽅法批量添加⽤户. 经过了上⾯的分
析后, 现在我们就可知道其实这⾥添加注解是不会启动事务功能的, 因为 AOP 逻辑整个都没⽣效嘛.
解决这个问题的⽅法有两个, ⼀个是使⽤ AspectJ 模式的事务实现:
<tx:annotation-driven mode="aspectj"/>
另⼀个就是和我们刚才在上⾯的例⼦中的解决⽅式⼀样:
public class UserService { private UserService self; public void setSelf(UserService self) { this.self = self; } @Transactional public boolean addUser(String userNa 参考:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。