AOP設計的初衷
- DRY:Don’t Repeat Yourself,減少重複代碼;
- SoC:Separation of Concerns,關注分離;
- 水平分離:展示層 --> 服務層 --> 持久層
- 垂直分離:模塊之間分離;
- 切面分離:功能性需求與非功能性需求分離;
使用AOP的好處
- 集中處理某一關注點/橫切邏輯
- 可以很方便的添加/刪除關注點
- 侵入性少,增強代碼可讀性以及可維護性
AOP應用場景
- 權限控制;
- 緩存控制;
- 事務控制;
- 審計日誌;
- 性能監控;
- 分佈式追蹤;
- 異常處理;
AOP術語介紹
- 通知(Advice):簡單的說就是你想要的功能,預先定義好這些功能,然後應用在你想要用的地方;
- 連接點(JoinPoint):spring允許你使用通知(Advice)的地方,通常可以是:每個方法調用的前後、拋出異常時等;
- 切入點(PointCut):切入點是建立在連接點基礎上的,例如有十個方法,每個方法的調用前後等都是連接點,但你只想在某個方法的調用前織入通知,那麼就可以使用切點來定義這個方法。通過切點對連接點進行定義,從而篩選出你想要織入通知的連接點;
- 切面(Aspect):切面是通知和切點的結合。通知定義了什麼時候幹什麼事,切點定義了在哪幹,通知和切點共同組成了完整的切面;
- 引入(Introduction):允許我們向現有的類中添加新的方法。結合上面的幾個術語,其實就是將切面應用到具體的目標類中;
- 目標(Target):即目標類,也就是被通知的對象,通常是真正的業務邏輯。目標類可以在毫不知情的情況下被織入切面,而自己專注於業務本身的邏輯;
- 代理(Proxy):AOP機制的實現就是通過代理完成的;
- 織入(Weaving):把切面應用到目標對象時創建新的代理對象的過程,有三種織入方法:編譯期織入,裝載期織入和運行時織入;
AOP註解介紹
- @Aspect :標註在類上,表示當前類是一個切面類;
- @PointCut:切入點;
- @Before:前置通知,即在某個連接點之前執行通知;
- @AfterReturning:後置通知,即在某個連接點正常完成後執行通知,通常在一個匹配的方法返回的時候執行;
- @AfterThrowing:異常通知,即在方法拋出異常退出時執行通知;
- @After:最終通知,即某個連接點退出時執行通知;
- @Around:環繞通知,它是最強大也是最麻煩的通知,它可以在方法調用前後完成自定義的行爲,它可以自己選擇是否繼續執行連接點或者直接返回或者拋出異常來結束執行;
切面表達式
切面表達式主要由三部分組成:
- designators(指示器):execution()等;
- wildcards(通配符): * … +
- operators(操作符):&& || !
wildcards(通配符)
- *:匹配任意數量的字符;
- +:匹配指定類及其子類;
- …:一般用於匹配任意數的子包或者參數;
operators(操作符)
- &&:與;
- ||:或
- !:非
designators(指示器)
匹配方法
- execution()
表達式結構:
execution(<修飾符>? <返回類型> <包名>?<方法名>(<參數>)異常?)
execution(
modifier-pattern?
returnType-pattern
package-pattern?
methodName-pattern(args-pattern)
throwException-pattern?
)
execution(*[修飾符]? *[返回值] *[包路徑]?*[方法名](..[參數])throw *[異常類]?)
- 修飾符匹配(modifier-pattern)
//匹配所有public修飾的方法
@Pointcut("execution(public * **(..))")
public void exTest1(){}
- 返回值匹配(returnType-pattern)
//匹配所有String或者void返回值的方法
@Pointcut("execution(String||void **(..))")
public void exTest2(){}
- 包路徑匹配(package-pattern)
//com包下的所有類的方法
@Pointcut("execution(* com.*.*(..))")
public void exTest3(){}
//如果不使用..匹配到了類級別的名字,需要類.方法名.............
//com包下的所有子包的所有類的方法
@Pointcut("execution(* com..*(..))")
public void exTest4(){}
//com包下的所有子包的ProductService類的方法
@Pointcut("execution(* com..ProductService.*(..))")
public void exTest5(){}
- 方法名匹配(methodName-pattern)
//匹配所有test名字開頭的方法
@Pointcut("execution(* test*(..))")
public void exTest6(){}
//匹配所有包含test名字的方法
@Pointcut("execution(* *test*(..))")
public void exTest7(){}
- 參數匹配(args-pattern)
//匹配所有參數列表的方法
@Pointcut("execution(* *(..))")
public void exTest8(){}
//匹配無參數列表的方法
@Pointcut("execution(* *())")
public void exTest9(){}
- 異常匹配(throwsException-pattern)
//匹配所有拋過異常的方法
@Pointcut("execution(* *()throws *)")
public void exTest10(){}
//只匹配所有拋出空指針異常的方法
@Pointcut("execution(* *()throws NullPointerException)")
public void exTest11(){}
匹配註解
- @target()
//匹配所有使用了AspectAnnotation註解的類的所有方法(要求註解的RetentionPolicy的級別爲RUNTIME)
@Pointcut("@target(com.tiglle.manage.AspectAnnotation)")
public void targetMatch(){}
- @args()
//匹配所有使用了AspectAnnotation註解爲參數的方法
@Pointcut("@args(com.tiglle.manage.AspectAnnotation)")
public void argsMatch(){}
- @winth()
//匹配所有使用了AspectAnnotation註解的類的所有方法(要求註解的RetentionPolicy的級別爲CLASS)
@Pointcut("@within(com.tiglle.manage.AspectAnnotation)")
public void withinMatch(){}
- @annotation()
//方法註解匹配,匹配所有帶AspectAnnotation註解的方法
@Pointcut("@annotation(com.tiglle.manage.annotation.AspectAnnotation)")
public void test(){
}
匹配包或者類型
- within()
- 如果傳的是全類名(包名.類名):匹配此類下所有的方法
//匹配TestService類中的所有方法
@Pointcut("within(com.tiglle.service.TestService)")
public voud test(){}
- 如果傳的時包名:匹配此包下所有類的方法
//匹配com/tiglle/包下所有包和子包中的類中的所有方法
@Pointcut("within(com.tiglle..*)")
public voud test(){}
匹配對象
- this()
//匹配代理對象和普通對象及其所有子類的方法
@Pointcut("this(com.tiglle.manage.service.ProductService)")
public void thisMatch(){
}
- bean()
//根據spring容器的bean的名稱(id)匹配,(不匹配子類)
@Pointcut("bean(productService)")
public void beanMatch(){
}
- target()
//匹配目標對象和普通對象及其所有子類的方法
@Pointcut("target(com.tiglle.manage.service.ProductService)")
public void targetMatch(){
}
匹配參數
- args()
//匹配spring容器所有此參數類型和列表的方法(String,Long)
@Pointcut("args(String,Long)")
public void argsMatch(){}
//匹配spring容器所有此參數類型和列表的方法(第一個爲Long,後面隨意)
@Pointcut("args(Long,..)")
public void argsMatch2(){}