AspectJ在Spring中的使用

在上一篇AspectJ的入門中,簡單的介紹了下AspectJ的使用,主要是以AspectJ的example做爲例子。介紹完後也留下了幾個問題:
1)咱們在spring中並無看到須要aspectj之類的關鍵詞,而是使用java代碼就能夠了,這是如何作到的
2)Spring中如何作到不使用特殊的編譯器實現aop的(AspectJ如何在運行期使用)
3)Spring源碼中與aspectJ 相關的AjType到底是啥?html

這篇文章會繼續試着解決這幾個問題。java

aspectJ的幾種織入方式

compile-time、post-compile 和 load-time Weavers

首先了解下AspectJ的幾種織入方式,分別是compile-time、post-compile 和 load-time,分別對應着編譯期、後編譯期、加載期織入spring

編譯期織入

首先是編譯期織入,上一篇博客所介紹的方式就是使用的編譯期織入。很容易理解,普通的java源碼aspectJ特殊語法的‘配置’ 文件 + aspectJ特殊的編譯器,編譯時候生成已織入後的.class文件,運行時直接運行便可。eclipse

後編譯期織入

後編譯期織入和編譯期的不一樣在於,織入的是class字節碼或者jar文件。這種形式,能夠織入一個已經織入過一次的切面。一樣這種狀況也須要特殊的編譯器post

加載期織入

加載期顧名思義,是在類被加載進虛擬機以前織入,使用這種方式,須使用AspectJ agent。性能

瞭解了這些概念,下面就要知道,spring是使用哪一種呢?spring哪種都不是,spring是在運行期進行的織入。學習

Spring 如何使用AspectJ

AspectJ 自己是不支持運行期織入的,平常使用時候,咱們常常回據說,spring 使用了aspectJ實現了aop,聽起來好像spring的aop徹底是依賴於aspectJthis

其實spring對於aop的實現是經過動態代理(jdk的動態代理或者cglib的動態代理),它只是使用了aspectJ的Annotation,並無使用它的編譯期和織入器,關於這個能夠看這篇文章 ,也就是說spring並非直接使用aspectJ實現aop的spa

spring aop與aspectJ的區別

看了不少篇博客以及源碼,我對spring aop與aspectJ的理解大概是這樣;
1)spring aop 使用AspectJ語法的一個子集,一些method call, class member set/get 等aspectJ支持的語法它都不支持
2)spring aop 底層是動態代理,因此受限於這點,有些加強就作不到,好比 調用本身的方法就沒法走代理代理

看下下面的例子:

@Component
public class A{
    public void method1(){
        method2();
    }

    public void method2(){
       //...
    }
}

這個時候method2是沒法被切到的,要想被切到能夠經過以下奇葩的方式:

@Component
public class A{

    @Autowired
    private A a;

    public void method1(){
        a.method2();
    }

    public void method2(){
       //...
    }
}

以前碰到這樣的問題時,我還特別不能理解,如今想下aop的底層實現方式就很容易理解了。

在以前寫的jdk動態代理與cglib動態代理實現原理,咱們知道了jdk動態代理是經過動態生成一個類的方式實現的代理,也就是說代理是不會修改底層類字節碼的,因此可能生成的代理方法是這樣的

public void method1(){
    //執行一段代碼
    a.method1()
    //執行一段代碼
}

public void method2(){
    //執行一段代碼
    a.method2()
    //執行一段代碼
}

回頭看a.method1()的源碼,也就明白了,爲啥method2()沒有被切到,由於a.method1()執行的方法,最後調用的不是 代理對象.method2(),而是它本身的method2()(this.method2()) 這個方法自己沒有任何改動

反觀aspectJ,aspectJ是在編譯期修改了方法(類自己的字節碼被改了),因此能夠很輕鬆地實現調用本身的方法時候的加強。

3)spring aop的代理必須依賴於bean被spring管理,因此若是項目沒有使用spring,又想使用aop,那就只能使用aspectJ了(不過如今沒有用spring的項目應該挺少的吧。。。)

4)aspectJ因爲是編譯期進行的織入,性能會比spring好一點
5)spring能夠經過@EnableLoadTimeWeaving 開啓加載期織入(只是知道這個東西,沒怎麼研究。。有興趣的能夠本身去研究下)
6)spring aop不少概念和aspectJ是一致的

AspectJ的註解在spring aop中的應用

瞭解了spring與aspectJ的關係後,就能更清晰的瞭解spring 的aop了。

先說明一點,雖然我介紹aspect的配置時,一直介紹的aspectJ文件配置方式,可是aspectJ自己是支持註解方式配置的。能夠看官方文檔,註解在aspectJ中的使用

而spring 使用了aspectJ註解的一小部分(正如前面所說的,受限於jdk的動態代理,spring只支持方法級別的切面)

回頭看看AjType

回頭看看以前看到的這段源碼,什麼是AjType,通過aspectJ解析器解析後對類的一種描述,好比正常的方法多是這樣

/*
 * 配置前置通知,使用在方法aspect()上註冊的切入點
 * 同時接受JoinPoint切入點對象,能夠沒有該參數
 */
@Before("aspect()")
public void before(JoinPoint joinPoint) {
    log.info("before " + joinPoint);
}

在AjType中就能獲取到不少其餘的aspectJ所需的相關信息(除了java反射所能獲取到的信息之外)

/**
 * Return the pointcut object representing the specified pointcut declared by this type
 */
public Pointcut getDeclaredPointcut(String name) throws NoSuchPointcutException;

/**
 * Return the pointcut object representing the specified public pointcut
 */
public Pointcut getPointcut(String name) throws NoSuchPointcutException;

好比看着兩個方法,能夠獲取到切入點信息。

在看看PerClauseKind.SINGLETON 這裏就複用了aspectJ的概念,詳細能夠看這篇文章最後部分

總結下

這篇文章回答了以前學習aspectJ時候碰到的幾個問題,而後討論了下aspectJ在spring中的應用。最大的收穫是瞭解了spring與aspectJ 的關係,瞭解了二者對aop的不一樣實現所形成的使用上的影響。之後當遇到了spring aop相關的概念若是不理解,能夠去aspectJ上去搜搜看了 。

參考文章:
Intro to AspectJ
spring 使用 load-time weaving
spring aop和 aspectJ 的比較



本文做者:端吉

閱讀原文

本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索