1. 程式人生 > >AOP和spring AOP學習記錄

AOP和spring AOP學習記錄

### AOP基本概念的理解 面向切面AOP主要是在編譯期或執行時,對程式進行織入,實現代理, 對原始碼毫無侵入性,不破壞主要業務邏輯,減少程式的耦合度。 - 主要應用範圍: 日誌記錄,效能統計,安全控制,事務處理,異常處理等等 #### 名詞性概念 - **切面(Aspect)** 通常是一個類,在裡面可以定義切入點和通知,即`切面=切入點+通知`。 - **連線點(Joint Point)** 被攔截到的點,因為 Spring 只支援方法型別的連線點,所以在 Spring 中連線點指的就是被攔截的到的方法,實際上連線點還可以是欄位或者構造器。 - **切入點(Pointcut)** 對連線點進行攔截的定義。 - **通知(Advice)** 攔截到連線點之後所要執行的程式碼,通知分為前置、後置、異常、最終、環繞通知五類。 - **AOP 代理** AOP 框架建立的物件,代理就是目標物件的加強。Spring 中的 AOP 代理可以使 JDK 動態代理,也可以是 CGLIB 代理,前者基於介面,後者基於子類。 - **織入**(Weaving) 把切面加入到物件,並創建出代理物件的過程。 #### 動態代理 在執行期間生成物件進行代理, spring AOP就是動態代理。 #### 靜態代理 自己編寫代理物件,在編譯器織入, AspectJ就是在編譯期間進行織入,從而減少對執行時效率的影響。 # SpringAOP 根據物件是否是實現類選擇代理的方法 - 如果要代理的物件實現了介面,spring AOP會根據介面,使用JDK Proxy建立代理 - 如果沒有實現介面,則通過CGLIB代理 當然也可以指定都使用CGLIB進行切入,而JDK方法不適用於沒有介面實現的目標類 #### 對於JDK proxy Jdk proxy會實現目標類的介面,代理邏輯就在新的實現類中 只能對實現類進行代理,效能也優於CGLIB,所以平時都是分為"介面-實現iml"來設計。 #### 對於CGLIB cglib會建立一個代理目標類的子類,而代理邏輯就在這一子類中新增, 然後得到這一子類的物件,也就是代理增強後的物件,一切都是動態的, 具體實現有待學習。 ## 切面類 使用@Aspect註解定義切面類 比如這樣 ```java @Component @Aspect @Oder(99) public class WebLogAspect {} ``` 另外對同一個方法有多個切面進行代理的時候,難免需要區分執行順序, 這時候可以使用@Order註解定義優先順序,數字越低,級別越高。 ## 切面Advice 執行順序around -> before -> around -> after -> afterRuternning #### @After 第一個引數都必須是JoinPoint型別 在目標方法完成後通知, 無論方法是以何種方式完成,異常也是如此 #### @After-returning 第一個引數都必須是JoinPoint型別 結束並安全返回時通知,異常不通知 #### @After-throwing 第一個引數都必須是JoinPoint型別 異常時通知 #### @Before 第一個引數都必須是JoinPoint型別 顧名思義 ####@Around 最強大的通知 第一個形參必須是ProceedingJoinPoint型別, ```java public interface ProceedingJoinPoint extends JoinPoint { void set$AroundClosure(AroundClosure var1); default void stack$AroundClosure(AroundClosure arc) { throw new UnsupportedOperationException(); } Object proceed() throws Throwable; Object proceed(Object[] var1) throws Throwable; } ``` 可以看到它繼承自JoinPoint,多了proceed方法 註解的方法體內呼叫ProceedingJoinPoint引數的**proceed()**方法才會執行目標。 呼叫這個方法時,還可以傳入一個Object[]物件作為引數 可以通過這個切面**決定方法是否執行,改變傳入引數,改變返回值,檢查異常等**。 #### 引數方法 - getArgs(): Returns the method arguments. - getThis(): Returns the proxy object. - getTarget(): Returns the target object. - getSignature(): Returns a description of the method that is being advised. - toString(): Prints a useful description of the method being advised. ## 切點表示式 有關切點表示式,建議閱讀 [傳送門](https://www.jianshu.com/p/a0b9c53ac019) 可以定義一個切點,之後就不用一一指定,直接帶入value即可,例如: ```java @Pointcut("execution(* com.hyg.app.controller..*.*(..))") public void webLog(){} @Before(value = "webLog()") public void doBefore (JoinPoint jp){ String name = jp.getSignature().getName(); System.out.println(name+"開始執行"); } ``` ### execution 表示式中最常用的方法是execution,粒度最小 對於`execution(* com.hyg.app.controller..*.*(..))` - `execution `表示式的型別指定 - 第一個`* `代表 任意的**返回值型別** - com.jiuxian aop所切的**包名** - 包後面`.. `表示**當前包**及**其子包**,一個`.`是當前包,兩個`.`就包括所有子包 - 第二個`*` 表示類名,代表所有類 - `.*(..) `表示任何方法, 括號代表引數 .. 表示任意引數 後面那串東西,可以記為每一個`.`都是更深的一個粒度 來個精確點的示例: `execution(* com.hyg.app.service.*Service.add*(String))` 表示`所有型別`的,`com.hyg.app.service`包下的, 所有以`Service`結尾的類, 以`add`開頭的,引數型別為`String`的方法 ### 例子 在官方文件中有很多示例,如下 ``` 所有public方法 execution(public * *(..)) 所有名字以set開頭的方法 execution(* set*(..)) 所有AccountService中實現的介面的方法 execution(* com.xyz.service.AccountService.*(..)) 所有service包下的方法 execution(* com.xyz.service.*.*(..)) 所有在service包以及子包下的方法 execution(* com.xyz.service..*.*(..)) within(com.xyz.service.*) 所有service包下方法 within(com.xyz.service..*) 所有service包和子包下方法 this(com.xyz.service.AccountService) 匹配accountservice代理的物件 target(com.xyz.service.AccountService) 實現了AccountService介面的物件 擁有transactional註解的 @annotation(org.springframework.transaction.annotation.Transactional) 傳遞的引數是Serializable的 args(java.io.Serializable): ``` ### 關於execution和within的區別 execution可以指定方法返回型別,類名,方法名和引數名等與方法相關的部件, 而within的最小粒度是類。 ### 關於this和target的區別 this匹配的是代理類,即目標物件被代理後的代理物件 target則是匹配普通的目標物件 以下情況外,二者的效果都是一致的。 this(SomeObject)或target(SomeObject),這裡SomeObject實現了某個介面:對於這種情況,雖然表示式中指定的是一種具體的物件型別,但由於其實現了某個介面,因而Spring預設會使用Jdk代理為其生成代理物件,Jdk代理生成的代理物件與目標物件實現的是同一個介面,但代理物件與目標物件還是不同的物件,由於代理物件不是SomeObject型別的,因而此時是不符合this語義的,而由於目標物件就是SomeObject型別,因而target語義是符合的,此時this和target的效果就產生了區別;這裡如果強制Spring使用Cglib代理,因而生成的代理物件都是SomeObject子類的物件,其是SomeObject型別的,因而this和target的語義都符合,其效果就是一致的。 ### args 用於指定引數型別,型別必須是全路徑的 ### 官網解釋 - `execution`: For matching method execution join points. This is the primary pointcut designator to use when working with Spring AOP. - `within`: Limits matching to join points within certain types (the execution of a method declared within a matching type when using Spring AOP). - `this`: Limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type. - `target`: Limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type. - `args`: Limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types. - `@target`: Limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type. - `@args`: Limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given types. - `@within`: Limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP). - `@annotation`: Limits matching to join points where the subject of the join point (the method being executed in Spring AOP) has the given annotation. [---官方文件---](https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop) 我的部落格:[https://www.seyana.life/post/12](https://www.seyana.life/p