Spring AOP 註解形式
AspectOriented Programing,面向切面編程。
AOP主要用於日誌記錄,性能統計,安全控制(權限控制),事務處理,異常處理等。將日誌記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來,通過對這些行為的分離,我們希望可以將它們獨立到非指導業務邏輯的方法中,進而改變這些行為的時候不影響業務邏輯的代碼。
Spring AOP織入增強(Advice)的方式有兩種 如果連接點實現了接口采用jdk自帶的動態代理的形式實現織入,如果連接點沒有實現接口則采用動態字節碼生成技術(CGLIB)實現織入。
AOP常用術語:
連接點(Joinpoint)
增強程序執行的某個特定位置(要在哪個地方做增強操作)。Spring僅支持方法的連接點,既僅能在方法調用前,方法調用後,方法拋出異常時等這些程序執行點進行織入增強。
切點(Pointcut)
切點是一組連接點的集合。AOP通過“切點”定位特定的連接點。通過數據庫查詢的概念來理解切點和連接點的關系再適合不過了:連接點相當於數據庫中的記錄,而切點相當於查詢條件。
增強(Advice)
增強是織入到目標類連接點上的一段程序代碼。表示要在連接點上做的操作。
切面(Aspect)
切面由切點和增強(引介)組成(可以包含多個切點和多個增強),它既包括了橫切邏輯的定義,也包括了連接點的定義,SpringAOP就是負責實施切面的框架,它將切面所定義的橫切邏輯織入到切面所指定的鏈接點中。
註解切面類例子:
@Aspect public class LogAspect { @Pointcut("execution(* com.ctj.service.*.*(..))") public void pointcutName(){} @Before("pointcutName()") public voidperformance(){ System.out.println("Spring AOP"); } }
常用註解:
- @aspect 定義切面
- @pointcut 定義切點
- @before 標註Before Advice定義所在的方法
- @afterreturning 標註After Returning Advice定義所在的方法
- @afterthrowing 標註After Throwing Advice定義所在的方法
- @after 標註 After(Finally) Advice定義所在的方法
- @around 標註Around Advice定義所在的方法
我們如何在定義切點(Pointcut)的時候指定一類Joinpoint呢?有兩種方式 簡單的方法名指定以及正則表達式兩種方式。
常用的@AspectJ形式Pointcut表達式的標誌符:
execution:
Spring AOP僅支持方法執行類型的Joinpoint 所以execution將會是我們用的最多的標誌符,用它來幫我們匹配擁有指定方法前面的Joinpoint。匹配規則如下:
execution(modifiers-pattern? return-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern)
- modifiers-pattern 修飾符 比如public private這種(可以指定可以不指定)
- return-type-pattern 返回值類型(必須指定)
- declaring-type-pattern 類型(可以是含包名的全路徑類型 可以指定可以不指定)
- name-pattern 方法名(必須指定)
- param-pattern 參數類型(必須指定)
方法的返回類型 方法名及參數部分的匹配模式是必須指定的 其他部分可以省略。
我們還可以在表達式中使用兩種通配符:*和..
第一:*可以用於任何部分的匹配模式中,匹配相鄰的多個字符,即一個Work 。如果放在了方法參數的位置標示參數是任何類型的。
例如:execution(* *(String))
第二:..通配符可以在兩個位置使用 一個是declaring-type-pattern的位置,一個是在方法參數匹配模式的位置。
如果是放在了方法類型的位置,可以指定多個層次的類型聲明。例如:
execution(void cn.spring.*.doSomething(*)) 指定到cn.spring下的所有類型。
如果是放在了方法參數的匹配位置,則表示該方法可以有0到多個參數。例如:
execution(void *.doSomething(..))
within:
within標誌符只接受類型聲明,它將匹配指定類型下所有的Joinpoint。
例如:within(cn.spring.aop.target.*) 將會匹配 cn.spring.aop.target包下所有類型的方法級別的Joinpoint。
@annotation
使用@annotation標誌符會檢查系統中所有對象的所有方法級別Joinpoint,如果被檢測的方法標註有@annotation標誌符所指定的註解類型,那麽當前方法所在的Joinpoint將被Pointcut表達式匹配。例如:@pointcut("@annotation(com.test.aop.log.ALog)") 匹配所有使用了ALog註解的方法。
匹配表達式的維度有很多 上面只是一小部分常用的,並且這些維度是可以組合的 使用||或者$$等等
例如:@around("within(com.test.finance..*) && @annotation(com.test.finance.platform.intf.base.db.ReadOnly)")
在定義Advice的時候 我們匹配的維度可以直接寫定義有@pointcut的方法名稱 也可以直接使用定義@joinpoint的那一套東西來直接定義要在哪些地方織入(可以直接在Advice上指定匹配哪些方法)
定義完切面之後我們要在spring中註冊這個切面類,為了讓spring能自動幫我們實現織入 我們還需要開啟自動註入 在spring配置文件中:<aop:aspectj-autoproxy proxy-target-class="true"/> 這樣spring就能在IOC容器找到所有要織入的方法 動態幫我們織入。
一個完整的Spring AOP的小例子:
業務類代碼:
package com.ctj.service; import org.springframework.stereotype.Service; @Service public class BusinessService { public void say(){ System.out.println("Business Code"); } }
切面類定義:
package com.ctj.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class LogAspect { @Pointcut("execution(* com.ctj.service.*.*(..))") public void pointcutName(){} @Before("pointcutName()") public void performance(){ System.out.println("Spring AOP"); } }
spring-aop.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"> <aop:aspectj-autoproxy proxy-target-class="true"/> <bean id="logAspect" class="com.ctj.aspect.LogAspect"> </bean> </beans>
基於註解的Spring AOP需要JDK1.5版本以後才能使用,之前的版本需要使用基於Schema也就是配置文件的形式來實現,如果jdk版本高的話 建議還是使用註解的形式。
Spring AOP 註解形式