实例:

@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyMonitor {
}
@Component
@Aspect
public class MyAopAdviseDefine {private Logger logger = LoggerFactory.getLogger(getClass());@Pointcut("@annotation(com.xys.demo4.MyMonitor)")public void pointcut() {}// 定义 advise@Before("pointcut()")public void logMethodInvokeParam(JoinPoint joinPoint) {logger.info("---Before method {} invoke, param: {}---", joinPoint.getSignature().toShortString(), joinPoint.getArgs());}
}
@Service
public class SomeService {private Logger logger = LoggerFactory.getLogger(getClass());public void hello(String someParam) {logger.info("---SomeService: hello invoked, param: {}---", someParam);test();}@MyMonitorpublic void test() {logger.info("---SomeService: test invoked---");}
}
@EnableAspectJAutoProxy(proxyTargetClass = true)
@SpringBootAppliMyion
public class MyAopDemo {@AutowiredSomeService someService;public static void main(String[] args) {SpringAppliMyion.run(MyAopDemo.class, args);}@PostConstructpublic void aopTest() {someService.hello("abc");}
}

在这个例子中, 我们定义了一个注解 MyMonitor, 这个是一个方法注解, 我们的期望是当有此注解的方法被调用时, 需要执行指定的切面逻辑, 即执行 MyAopAdviseDefine.logMethodInvokeParam 方法.

在 SomeService 类中, 方法 test() 被 MyMonitor 所注解, 因此调用 test() 方法时, 应该会触发 logMethodInvokeParam 方法的调用. 不过有一点我们需要注意到, 我们在 MyAopDemo 测试例子中, 并没有直接调用 SomeService.test() 方法, 而是调用了 SomeService.hello() 方法, 在 hello 方法中, 调用了同一个类内部的 SomeService.test() 方法. 按理说, test() 方法被调用时, 会触发 AOP 逻辑, 但是在这个例子中, 我们并没有如愿地看到 MyAopAdviseDefine.logMethodInvokeParam 方法的调用, 这是为什么呢?

这是由于 Spring AOP (包括动态代理和 CGLIB 的 AOP) 的限制导致的. Spring AOP 并不是扩展了一个类(目标对象), 而是使用了一个代理对象来包装目标对象, 并拦截目标对象的方法调用. 这样的实现带来的影响是: 在目标对象中调用自己类内部实现的方法时, 这些调用并不会转发到代理对象中, 甚至代理对象都不知道有此调用的存在.

即考虑到上面的代码中, 我们在 MyAopDemo.aopTest() 中, 调用了 someService.hello("abc"), 这里的 someService bean 其实是 Spring AOP 所自动实例化的一个代理对象, 当调用 hello() 方法时, 先进入到此代理对象的同名方法中, 然后在代理对象中执行 AOP 逻辑(因为 hello 方法并没有注入 AOP 横切逻辑, 因此调用它不会有额外的事情发生), 当代理对象中执行完毕横切逻辑后, 才将调用请求转发到目标对象的 hello() 方法上. 因此当代码执行到 hello() 方法内部时, 此时的 this 其实就不是代理对象了, 而是目标对象, 因此再调用 SomeService.test() 自然就没有 AOP 效果了.

简单来说, 在 MyAopDemo 中所看到的 someService 这个 bean 和在 SomeService.hello() 方法内部上下文中的 this 其实代表的不是同一个对象(可以通过分别打印两者的 hashCode 以验证), 前者是 Spring AOP 所生成的代理对象, 而后者才是真正的目标对象(SomeService 实例).

解决:

弄懂了上面的分析, 那么解决这个问题就十分简单了. 既然 test() 方法调用没有触发 AOP 逻辑的原因是因为我们以目标对象的身份(target object) 来调用的, 那么解决的关键自然就是以代理对象(proxied object)的身份来调用 test() 方法.
因此针对于上面的例子, 我们进行如下修改即可:

@Service
public class SomeService {private Logger logger = LoggerFactory.getLogger(getClass());@Autowiredprivate SomeService self;public void hello(String someParam) {logger.info("---SomeService: hello invoked, param: {}---", someParam);self.test();}@CatMonitorpublic void test() {logger.info("---SomeService: test invoked---");}
}

上面展示的代码中, 我们使用了一种很 subtle 的方式, 即将 SomeService bean 注入到 self 字段中(这里再次强调的是, SomeService bean 实际上是一个代理对象, 它和 this 引用所指向的对象并不是同一个对象), 因此我们在 hello 方法调用中, 使用 self.test() 的方式来调用 test() 方法, 这样就会触发 AOP 逻辑了.

原文

在同一个类中调用另一个方法没有触发 Spring AOP 的问题相关推荐

  1. SpringCache @Cacheable 在同一个类中调用方法,导致缓存不生效的问题及解决办法...

    由于项目需要使用SpringCache来做一点缓存,但自己之前没有使用过(其实是没有听过)SpringCache,于是,必须先学习之. 在网上找到一篇文章,比较好,就先学习了,地址是: https:/ ...

  2. python类中调用另一个程序函数_Python:如何在另一个类中调用函数

    我被一些python脚本困住了 https://python4kids.brendanscott.com/2014/12/02/hooking-up-the-sunfish-chess-engine- ...

  3. java如何调用repaint_java – 在另一个类中调用的Repaint()方法

    超级工作正常,但你对它的作用的解释是错误的.您的问题是您正在尝试使用继承来解决未通过继承解决的问题.你需要在实际可视化和使用的Ball实例上调用repaint(),而不是在一个完全不同的类Key的实例 ...

  4. java方法中与参数怎么调用,java中怎么从一个方法中调用另一个方法中的参数?(以及如何提取数字)...

    一.使用动态方法 public int comparisonOfTotalVehicles (WebDriver webDriver)throws InterruptedException{ WebE ...

  5. java 调用其他构造函数_我如何在Java中调用另一个构造函数?

    是的,这是可能的: public class Foo { private int x; public Foo() { this(1); } public Foo(int x) { this.x = x ...

  6. php引入类的位置,php如何在一个类中引入另外一个类

    有时候需要在一个类中调用另外一个类里面的方法,然后另外一个类又需要调用当前类的方法,怎么办呢?下面是具体的介绍.希望对大家有用,更多内容请关注应届毕业生网! 可以直接引入类对象的方式调用另外一个类的方 ...

  7. Spring事务管理嵌套事务详解 : 同一个类中,一个方法调用另外一个有事务的方法

    Spring 事务机制回顾 Spring事务一个被讹传很广说法是:一个事务方法不应该调用另一个事务方法,否则将产生两个事务.  结果造成开发人员在设计事务方法时束手束脚,生怕一不小心就踩到地雷.    ...

  8. 在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解失效的原因和解决方法

    在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解失效的原因和解决方法 参考文章: (1)在同一个类中,一个方法调用另外一个有注解(比如@Async, ...

  9. spring同一个类中,一个方法调用另外一个注解(@Transactional)方法时,注解失效

    title: "spring同一个类中,一个方法调用另外一个注解(@Transactional)方法时,注解失效" url: "https://wsk1103.githu ...

最新文章

  1. Java的字符串常量池
  2. 06.学习笔记-用户信息管理之操作命令
  3. Linux上常用的安全技术iptables与squid代理服务器
  4. 正儿八经的详细讲java内部类
  5. Hadoop之Hadoop企业优化(HDFS小文件优化)
  6. mysql好玩的代码_mysql的order by与where出现的好玩事
  7. 结对编程作业——毕设导师智能匹配
  8. 如何查看apache,php,mysql的编译参数
  9. 100%移植阿里云移动测试技术,竟仅需1周?! ——移动测试专有云(1)
  10. bootstap-栅格系统
  11. Ubuntu下添加boost库
  12. 06-20210308华为海思Hi3516DV300鸿蒙系统的uboot编译
  13. MIPS中addi指令被分成三条指令的问题
  14. Python添加flac文件标签并实现wav转flac
  15. Oracle数据库上机练习6
  16. 计算机存储器分级结构,存储器
  17. 【设计】资料合集(1-121)副业学习会
  18. SQL面试题练习记录
  19. 对项目工时的估算----( PERT “计划评审技术” ) 三点估算法
  20. ms 真空层_Materials Studio学习

热门文章

  1. xpath定位中详解id 、starts-with、contains、text()和last() 的
  2. 1.10.返回四舍五入后的值.round()
  3. HDU 5400 Arithmetic Sequence
  4. 探秘采云间:全链路数据处理工具直击传统DW/BI痛点
  5. POJ - 2182 Lost Cows【线段树】
  6. 省、省、省!!!企业如何搭建易用性网络
  7. javascript的一些基本概念
  8. nginx ssl 双向认证
  9. DTS开发记录(5)-- 挑战增量导出
  10. DNS劫持DNSmasq详细解析及详细配置