AOP 1,意爲:面向切面編程,經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。基於AOP實現的功能不會破壞原來程序邏輯,所以它能夠很好的對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。css
pom.xml
中加入 aop 依賴:<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
CGLIB
來實現 AOP,在系統配置文件中加入設置:spring:
aop:
proxy-target-class: true
默認爲 false
。java
列出經常使用的幾個表達式:
1. execution()
知足execution中描述的方法簽名。 execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
* modifier-pattern
:表示方法的修飾符;
* ret-type-pattern
:表示方法的返回值
* declaring-type-pattern
:表示方法所在的類的路徑
* name-pattern
:表示方法名
* param-pattern
:表示方法的參數
* throws-pattern
:表示方法拋出的異常
* 其中後面跟着?
的是可選項。spring
this()
是用來限定方法所屬的類,爲接口則限定全部的實現類,爲類的話,限定這單個類。@annotation
表示具備某個標註的方法。args
表示方法的參數屬於一個特定的類,@args
表示參數有特定的標註註解。within
包或者類型知足within中描述的包或者類型的類的全部非私有方法,@within
類型擁有@target描述中給出的annotation,其中@target和@within的區別在於@within要求的annotation的級別爲CLASS,而@target爲RUNTIME target
業務實例對象(非代理實例)的類型知足target 中的描述的類型,@target 類型擁有@target描述中給出的annotationbean()
表示全部匹配的 bean,例如 ,bean(「*Service」),匹配全部 Service
結尾的類。可使用 !bean()
表示不匹配。注意事項:
* 在各個pattern中,可使用」*」來表示匹配全部。
* 在param-pattern
中,能夠指定具體的參數類型,多個參數間用,
隔開,各個也能夠用 *
來表示匹配任意類型的參數,如(String)
表示匹配一個String
參數的方法;(*,String)
表示匹配有兩個參數的方法,第一個參數能夠是任意類型,而第二個參數是String類型。
* 能夠用(..)
表示零個或多個任意的方法參數。
* 使用&&
符號表示與關係,使用||
表示或關係、使用!
表示非關係。在XML文件中使用and
、or
和not
這三個符號。編程
AspectJ提供了五種定義通知的標註:springboot
@Before
:前置通知,在調用目標方法以前執行通知定義的任務@After
:後置通知,在目標方法執行結束後,不管執行結果如何都執行通知定義的任務@AfterReturning
:後置通知,在目標方法執行結束後,若是執行成功,則執行通知定義的任務@AfterThrowing
:異常通知,若是目標方法執行過程當中拋出異常,則執行通知定義的任務@Around
:環繞通知,在目標方法執行前和執行後,都須要執行通知定義的任務經過標註定義通知只須要兩個步驟:
1. 將以上五種標註之一添加到切面的方法中
2. 在標註中設置切點的定義。markdown
環繞通知相比其它四種通知有其特殊之處。環繞通知本質上是將前置通知、後置通知和異常通知整合成一個單獨的通知。函數
用@Around
標註的方法,該方法必須有一個ProceedingJoinPoint
類型的參數,spring-boot
在方法體中,須要經過這個參數,以joinPoint.proceed();
的形式調用目標方法。注意在環繞通知中必須進行該調用,不然目標方法自己的執行就會被跳過。this
計算方法的執行時間:spa
@Around("logPointCut()") //切點
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//執行方法
Object result = point.proceed();
//執行時長(毫秒)
long time = System.currentTimeMillis() - beginTime;
return result;
}
獲取參數的方式則須要使用關鍵詞是args
。
@Pointcut("bean(sysUserServiceImpl) && args(userEntity,..)")
public void userPointCut(SysUserEntity userEntity) {
}
@Before("userPointCut(userEntity)")
public void validateUser(SysUserEntity userEntity) {
// to handler args
}
這裏有個很是嚴格的一點就是,args(userEntity,..)
,表示目標方法,可能有多個參數,可是包括 userEntity
,這裏 userEntity
必須參數名相同,不一樣就編織了。
args()
中參數的名稱必須跟切點方法的簽名中public void validateUser(SysUserEntity userEntity)
的參數名稱相同。若是使用切點函數定義,其中的參數名稱也必須與通知方法簽名中的參數徹底相同
@AfterReturning(pointcut = "logPointCut()", returning = "rtv")
public void logAfter(Object rtv) {
System.out.println(Objects.toString(rtv));
}
使用 @AfterReturning
註解時,指定了一個returning
屬性,假設該屬性值爲rvt
,這代表容許在Advice方法(logAfter()方法)中定義名爲rvt的形參,程序可經過rvt形參來訪問目標方法的返回值。
注意:
雖然AfterReturning
加強處理能夠訪問到方法的返回值,但它不能夠改變目標方法的返回值。
有時候,咱們對一個方法會有多個切面的問題,這個時候還會涉及到切面的執行順序的問題。
咱們能夠定義每一個切面的優先級, Spring 中提供註解 @Order(i)
,當 i
的值越小,優先級越高。