前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >完整剖析SpringAOP的自调用

完整剖析SpringAOP的自调用

作者头像
方丈的寺院
发布2019-09-18 14:38:32
6620
发布2019-09-18 14:38:32
举报
文章被收录于专栏:方丈的寺院方丈的寺院

摘要

spring全家桶帮助java web开发者节省了很多开发量,提升了效率。但是因为屏蔽了很多细节,导致很多开发者只知其然,不知其所以然,本文就是分析下使用spring的一些注解,不能够自调用的问题。因为本身这类文章很多,所以有些地方不会详述,直接引用其他文章。

问题

  1. 使用了Spring中哪些注解不能进行自调用
  2. 为什么代理了就不能自调用
  3. Spring常用的 @Cache, @Async@Transaction 这三种原理上有什么区别吗
  4. 如何解自调用的问题
  5. 使用不同的解法各自有什么坑

AOP的概述

首先需要澄清几个需要区分的名词 AOP SpringAOP AspectJ

AOP

Aspect-oriented programming,面向切面编程,一种解决问题的思想,将一些重复性的编码问题通过切面来实现。很多人了解切面是通过Spring来了解的,所以会有种误解将SpringAOP和AOP划等号,其实不然。

Spring AOP

Spring AOP 算是一种简单的AOP的落地实现方式,它主要提供在Spring容器内的一种AOP实现方式,脱离了Spring就不work了。Spring AOP并不是一套完整的AOP解决方案。

Spring的的众多组件都是这样,Spring-Session,Spring-jdbc,Spring-Cache等等,都能解决一部分通用的需求,但是会有很多限制, 想用深了,更灵活的实现功能,还是要使用其他的专业组件/框架。

SpringAOP默认使用代理模式实现的,也就是JDK Proxy/CGLib。关于代理以及JDK Proxy和CGLib不在赘述了。

AspectJ

Spring AOP并不是一套完整的AOP解决方案,AspectJ是的。AspectJ在编译器织入切面到目标类

解法

上面介绍了SpringAop的实现,下面着重介绍解法。

方法1 - 注入代理bean到自己

这个原理没啥好解析的

代码语言:javascript
复制
@Autowired
    @Lazy
    private AsyncMethod asyncMethod;
      public void testAsync() {
        System.out.println(Thread.currentThread().getName());
        // 调用注入的bean
        asyncMethod.testAsnc3();
    }
    @Async
    public void testAsnc3() {
        System.out.println(Thread.currentThread().getName());
        System.out.println("async3");
    }

Note

会有循环依赖的问题,使用 @Lazy解决

方法2 - AopContext.currentProxy() 获取当前代理对象

使用

首先需要配置 @EnableAspectJAutoProxy(exposeProxy=true),允许代码中获取proxy类

代码语言:javascript
复制
public void testAsync() {
        System.out.println(Thread.currentThread().getName());
        System.out.println("async1");
      ((AsyncMethod)AopContext.currentProxy()).testAsnc2();
    }
    @Async
    public void testAsnc2() {
        System.out.println(Thread.currentThread().getName());
        System.out.println("async2");
    }

原理解析

这个实现可以看下AopContext类,

代码语言:javascript
复制
// 通过ThreadLocal来实现的
private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<Object>("Current AOP proxy");

然后就是Spring Aop自动设置代理,设置 exposeProxy属性的问题了。有人写过了,就不写了

https://cloud.tencent.com/developer/article/1497700

Note

  1. 因为使用了SpringAOP,所以会有代理模式的限制
  2. AopContext.currentProxy()使用的是ThreadLocal的,所以不能跨线程了
  3. bean设置的限制,比如@Async代理创建方式不同其他|方式

方法3 - 直接使用AspectJ

既然自调用的问题是由于SpringAOP由代理模式实现引起的,那就不使用代理模式不就解决了吗

使用

  1. 切换为代理模式
代码语言:javascript
复制
@EnableAsync(mode = AdviceMode.ASPECTJ)
  1. 添加aspectj织入包依赖
代码语言:javascript
复制
<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.8</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-instrument -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-instrument</artifactId>
            <version>4.2.5.RELEASE</version>
        </dependency>
  1. 使用
代码语言:javascript
复制
public void testAsync() {
        System.out.println(Thread.currentThread().getName());
        System.out.println("async1");
        testAsnc2();
    }

    /**
     * 测试ASPECTJ调用
     */
    @Async
    private void testAsnc2() {
        System.out.println(Thread.currentThread().getName());
        System.out.println("async2");
    }
  1. 启动方式 AspectJ是编译器将切面织入到目标class的,启动的使用需要加上java agent的参数
代码语言:javascript
复制
-Dserver.port=1000 -javaagent:${classpath}\spring-instrument-4.2.5.RELEASE.jar
-javaagent:${classpath}\aspectjweaver-1.8.8.jar

总结

方法

限制

自调用

代理模式的限制,比如只能作用于public ,非static的方法

AopContext.currentProxy()

1. 代理模式的限制 2.ThreadLocal的限制,不能跨线程了 3.bean设置的限制,比如@Async代理创建方式不同其他

AspectJ

无限制,使用起来麻烦一点

关注公众号【方丈的寺院】,第一时间收到文章的更新,与方丈一起开始技术修行之路

参考

http://blog.kezhuw.name/2017/08/31/spring-aspectj-load-time-weaving/

https://cloud.tencent.com/developer/article/1497700

https://frightanic.com/software-development/spring-proxy-self-invocation/

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/ProxyAsyncConfiguration.html#asyncAdvisor--

https://www.baeldung.com/spring-aop-vs-aspectj

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 方丈的寺院 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 摘要
  • 问题
  • AOP的概述
    • AOP
      • Spring AOP
        • AspectJ
        • 解法
          • 方法1 - 注入代理bean到自己
            • Note
          • 方法2 - AopContext.currentProxy() 获取当前代理对象
            • 使用
            • 原理解析
            • Note
            • 使用
        • 方法3 - 直接使用AspectJ
        • 总结
        • 参考
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档