完整剖析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-…

相關文章
相關標籤/搜索