完整剖析SpringAOP的自调用

摘要

spring全家桶帮助java web开发者节省了不少开发量,提高了效率。可是由于屏蔽了不少细节,致使不少开发者只知其然,不知其因此然,本文就是分析下使用spring的一些注解,不可以自调用的问题。由于自己这类文章不少,因此有些地方不会详述,直接引用其余文章。html

问题

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

AOP的概述

首先须要澄清几个须要区分的名词 AOP Spring AOP AspectJjava

AOP

Aspect-oriented programming,面向切面编程,一种解决问题的思想,将一些重复性的编码问题经过切面来实现。 不少人了解切面是经过Spring来了解的,因此会有种误解将SpringAOP和AOP划等号,其实否则。web

Spring AOP

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

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

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

在这里插入图片描述

AspectJ

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

解法

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

方法1 - 注入代理bean到本身

这个原理没啥好解析的async

@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属性的问题了。 有人写过了,就不写了

cloud.tencent.com/developer/a…

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 无限制,使用起来麻烦一点

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

在这里插入图片描述

参考

blog.kezhuw.name/2017/08/31/…

cloud.tencent.com/developer/a…

frightanic.com/software-de…

docs.spring.io/spring-fram…

www.baeldung.com/spring-aop-…

相关文章
相关标签/搜索