zoukankan      html  css  js  c++  java
  • 完整剖析SpringAOP的自调用

    摘要

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

    问题

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

    AOP的概述

    首先需要澄清几个需要区分的名词 AOP Spring AOP 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到自己

    这个原理没啥好解析的

        @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类

     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类,

    // 通过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. 切换为代理模式
    @EnableAsync(mode = AdviceMode.ASPECTJ)
    
    1. 添加aspectj织入包依赖
     <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. 使用
     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的参数
    -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

  • 相关阅读:
    调试
    node笔记汇总
    移动端布局
    css 易错点总结
    Angular笔记
    CANVAS笔记
    http笔记汇总
    各种环境搭建 软件安装等等 参考网址收录
    js中同步异步,任务队列
    node.js之fs模块
  • 原文地址:https://www.cnblogs.com/stoneFang/p/11529623.html
Copyright © 2011-2022 走看看