大多數的企業應用都會有一些共同的對橫切的關注,橫切是否适用于不同的對象或者模型。一些共同關注的橫切有日志、事務管理以及資料校驗等等。在面向對象的程式設計中,應用子產品是有類來實作的,然而面向切面的程式設計的應用子產品是由切面(aspect)來擷取的,他們被配置用于切不同的類。
aop 任務将橫切任務的直接依賴從類中抽離出來,因為我們不能直接從面向對象程式設計的模型中擷取這些依賴。例如,我們可以有一個單獨的類用于記錄日志,但是相同功能的類将不得不調用這些方法去擷取應用的中的日志。
在我們深入了解 spring 架構中 aop 的實作方式之前,我們需要了解 aop 中的一些核心概念。
<code>aspect</code>:切面,實作企業應用中多個類的共同關注點,例如事務管理。切面可以是使用 spring xml 配置的一個普通類或者是我們使用 spring aspectj 定義的一個标有 @aspect 注解的類。
<code>join point</code>:連接配接點,一個連接配接點是應用程式中的一個特定的點,例如方法執行、異常處理、改變對象變量的值等等。在 spring aop 中,一個連接配接點永遠是指一個方法的執行。
<code>advice</code>:通知,通知是指在一個特别的連接配接點上發生的動作。在程式設計中,他們是當一個連接配接點比對到一個切入點時執行的方法。你可以把通知想成 strust2 中的攔截器或者是 servlet 中的過濾器。
<code>pointcut</code>:切入點,切入點是一些表達式,當比對到連接配接點時決定通知是否需要執行。切入點使用不同種類型的表達式來比對連接配接點,spring 架構使用 aspectj 表達式文法。
<code>target object</code>:通知執行的目标對象。spring aop 是使用運作時的代理來實作的,是以該對象永遠是一個代理對象。這意味着一個子類在運作期間會被建立,該類的目标方法會被覆寫并且通知會基于他們的配置被引入。
<code>aop proxy</code>:spring aop 實作使用 jdk 的動态代理來建立包含有目标類和通知調用的代理類,這些類被稱為 aop 代理類。我們也可以使用 cglib 代理來作為 spring aop 項目的依賴實作。
<code>weaving</code>:織入,将切面和其他用于建立被通知的代理對象的類聯系起來的過程。這可以是在運作時完成,也可以是在代碼加載過程中或者運作時。spring aop 是在運作時完成織入的過程。
基于通知的執行政策,這裡有以下幾種通知類型:
<code>before advice</code>:在連接配接點方法執行之前運作。我們可以使用 <code>@before</code> 注解來标記一個通知類型為 before advice。
<code>after (finally) advice</code>:在連接配接點方法執行完成之後運作。我們可以使用 <code>@after</code> 來建立一個 after (finally) advice。
<code>after returning advice</code>:在方法傳回之後運作,通過 <code>@afterreturning</code> 注解建立。
<code>after throwing advice</code>:在方法抛出異常之後運作,通過 <code>@afterthrowing</code> 注解建立。
<code>around advice</code>:這是最重要和最強的通知。這個通知在連接配接點方法前後運作并且我們可以決定該通知是否運作,通過 <code>@around</code> 注解建立。
上面提到的知識點可能會使我們困惑,但是當我們看到 spring aop 的實作之後,就會豁然開朗了。下面我們來建立一個 spring aop 的項目。spring 支援使用 aspectj 的注解來建立切面,為了簡單,我們将直接使用這些注解。上面提到的所有 aop 的注解都定義在 org.aspectj.lang.annotation 包中。
建立一個簡單的 spring maven 項目,通過 pom.xml 引入 spring 的核心庫。在項目建立成功之後,我們可以看到下面的目錄結構:
spring 架構預設提供了對 aop 的支援,既然我們需要使用 aspectj 的注解,則需要在 pom.xml 中引入相關的依賴:
需要注意的是,我在項目中添加了對 aspectjrt 和 aspectjtools (版本為 1.7.4)的依賴。并且我也把 spring 的版本更新到了 4.0.2.release。
下面我們來建立一個簡單的 java bean:
employee.java
你有注意到 <code>setname()</code> 方法上定義了一個 <code>loggable</code> 注解嗎?它是一個我們項目中建立的自定義注解。我們将在後面介紹它的用法。
下面,我們來建立一個服務類來處理 employee 對象:
employeeservice.java
我本來可以使用 spring 注解來将其配置為一個 spring 的元件,但是在該項目中我們将會使用 xml 來配置。employeeservice 是一個非常标準的類,并提供了一個通路 employee 的點。
我項目中的配置 spring.xml 如下:
在 spring beans 中使用 aop,我們需要添加:
申明 aop 命名空間,如:<code>xmlns:aop="http://www.springframework.org/schema/aop"</code>。
添加 <code>aop:aspectj-autoproxy</code> 節點開啟 spring aspectj 在運作時自動代理的支援。
配置 aspect 類。
employeeaspect.java:
上面例子中重要的地方說明如下:
aspect 類需要添加 <code>@aspect</code> 注解。
<code>@before </code>注解用于建立 before advice。
<code>@before</code> 注解中的字元串參數是 pointcut 表達式。
getnameadvice() 通知将會在任何帶有 <code>public string getname()</code>` 方法簽名的 spring bean 方法執行時執行。這點是非常重要的,如果我們使用 new 操作符來建立一個 employee bean,該通知并不會執行,其隻會在 applicationcontext 擷取該 bean 時執行。
有時候,我們需要在多個地方上使用相同的切點表達式,我們可以使用一個空方法的 <code>@pointcut</code> 注解,然後在通知中将它作為表達式來使用。
employeeaspectpointcut.java
上面的例子非常清晰,相對于表達式,我們在使用方法名稱作為注解的參數。
我們可以使用 joinpoint 作為通知方法的參數并且使用他擷取方法簽名或者目标對象。
我們可以在連接配接點中使用 <code>args()</code> 表達式來比對任何方法的任何參數。如果我們使用它,則我們需要在通知方法中使用同參數相同的名稱。我們也可以在通知參數中使用泛型。
employeeaspectjoinpoint.java
employeeafteraspect.java 如下:
我們可以在切點表達式中使用 <code>within</code> 來申明該通知會在一個類的所有方法上執行。
正如前面提到的,我們可以使用 around aspect 來定義在方法前後進行執行指定的代碼。
employeearoundaspect.java
前面提到了 <code>@loggable</code> 注解,其定義如下:
我們可以建立一個切面來使用該切點,employeeannotationaspect.java 如下:
<code>myadvice()</code> 方法僅僅會在 <code>setname()</code> 方法執行前執行。
如果我們使用 sping 的配置檔案來定義切面,則定義方式如下。
employeexmlconfigaspect.java
在 配置檔案中定義如下:
最後,來看看一個簡單的程式來說明切面如何作用在 bean 的方法上。
我們将看到如下輸出:
你将會看到通知将會基于切點配置一個個的執行。你應該一個個的配置他們,以免出現混亂。
上面是 spring aop 教程的所有内容,我希望你了解了 spring aop 的基本概念并能從例子中學習到更多。你可以從下面連結下載下傳本文中的項目代碼。
<a href="http://www.journaldev.com/?wpdmact=process&did=mjauag90bgluaw==" target="_blank">download spring aop project</a>