AspectJ详解
文章目錄
- Spring AOP
- AspectJ
- 引入aspectj的相關jar包
- 在spring中啟用aspectj
- 編寫注解
- 自定義實現Aspect
- Pointcut
- #execution表示式(方法描述匹配)
- 方法參數匹配
- 當前AOP代理對象類型匹配
- 目標類匹配
- 標有此注解的方法匹配
- 在目標方法上添加注解
Spring AOP
AOP使用場景:權限控制、異常處理、緩存、事務管理、日志記錄、數據校驗等等
AOP基本概念
- 切面(Aspect): 程序運行過程中的某個的步驟或者階段
- 連接點(Joinpoint): 程序運行過程中可執行特定處理(增強處理)的點, 如異常處理。而在SpringAOP中,方法調用是連接點。
- Advice(通知、處理、增強處理): 在符合的連接點進行的特定處理 (增強處理)
- 切入點(Pointcut): 可切入進行增強處理的連接點。AOP核心之一就是如何用表達式來定義符合的切入點。在Spring中,默認使用AspectJ的切入點語法。
由于Spring AOP只支持以Spring Bean的方法調用來作為連接點, 所以在這里切入點的定義包括:
- 切入點表達式, 來限制該能作用的范圍大小,即是,能匹配哪些bean的方法
- 命名切入點
AspectJ
參考:Spring AOP @Before @Around @After 等 advice 的執行順序
執行順序
無異常情況:Around->Before->自己的method->Around->After->AfterReturning
異常情況:Around->Before->自己的method->Around->After->AfterThrowing
多個Aspect作用于一個方法上,如何指定每個 aspect 的執行順序呢?
方法有兩種:
- 實現org.springframework.core.Ordered接口,實現它的getOrder()方法
- 給aspect添加@Order注解,該注解全稱為:org.springframework.core.annotation.Order
注意點
- 如果在同一個 aspect 類中,針對同一個 pointcut,定義了兩個相同的 advice(比如,定義了兩個 @Before),那么這兩個 advice 的執行順序是無法確定的,哪怕你給這兩個 advice 添加了 @Order 這個注解,也不行。這點切記。
- 對于@Around這個advice,不管它有沒有返回值,但是必須要方法內部,調用一下 pjp.proceed();否則,Controller 中的接口將沒有機會被執行,從而也導致了 @Before這個advice不會被觸發。
使用步驟
引入aspectj的相關jar包
<!--使用AspectJ方式注解需要相應的包--> <dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>${aspectj.version}</version> </dependency> <!--使用AspectJ方式注解需要相應的包--> <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>${aspectj.version}</version> </dependency>在spring中啟用aspectj
<aop:aspectj-autoproxy proxy-target-class="true" />編寫注解
import java.lang.annotation.*;/*** api攔截器,記錄日志*/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented//文檔生成時,該注解將被包含在javadoc中,可去掉 public @interface ApiInf { }自定義實現Aspect
注意注解@Aspect,如果沒有使用@Component,則需要在spring中注冊該aspect的bean
<bean class="com.ufgov.util.rest.ApiLogAspect" />參考:Spring AOP 中@Pointcut的用法
Pointcut
@Pointcut:Pointcut的定義包括兩部分:Pointcut表達式(expression:就是@Pointcut后邊的參數)和Pointcut簽名(就是@Pontcut所在的方法名稱),Pointcut簽名在下邊@Before和@After等中使用(相當于使用Pointcut表達式)。
#execution表示式(方法描述匹配)
表達式類型
標準的Aspectj Aop的pointcut的表達式類型是很豐富的,但是Spring Aop只支持其中的9種,外加Spring Aop自己擴充的一種一共是10種類型的表達式,分別如下。
- execution:一般用于指定方法的執行,用的最多。
- within:指定某些類型的全部方法執行,也可用來指定一個包。
- this:Spring Aop是基于代理的,生成的bean也是一個代理對象,this就是這個代理對象,當這個對象可以轉換為指定的類型時,對應的切入點就是它了,Spring Aop將生效。
- target:當被代理的對象可以轉換為指定的類型時,對應的切入點就是它了,Spring Aop將生效。
- args:當執行的方法的參數是指定類型時生效。
- @target:當代理的目標對象上擁有指定的注解時生效。
- @args:當執行的方法參數類型上擁有指定的注解時生效。
- @within:與@target類似,看官方文檔和網上的說法都是@within只需要目標對象的類或者父類上有指定的注解,則@within會生效,而@target則是必須是目標對象的類上有指定的注解。而根據筆者的測試這兩者都是只要目標類或父類上有指定的注解即可。
- @annotation:當執行的方法上擁有指定的注解時生效。
- bean:當調用的方法是指定的bean的方法時生效。
其實execution表示式的定義方式就是方法定義的全量方式
格式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)括號中各個pattern分別表示:
- 修飾符匹配(modifier-pattern?)
- 返回值匹配(ret-type-pattern)可以為*表示任何返回值,全路徑的類名等
- 類路徑匹配(declaring-type-pattern?)
- 方法名匹配(name-pattern)可以指定方法名 或者 代表所有, set 代表以set開頭的所有方法
- 參數匹配((param-pattern))可以指定具體的參數類型,多個參數間用“,”隔開,各個參數也可以用“”來表示匹配任意類型的參數,如(String)表示匹配一個String參數的方法;(,String) 表示匹配有兩個參數的方法,第一個參數可以是任意類型,而第二個參數是String類型;可以用(…)表示零個或多個任意參數
- 異常類型匹配(throws-pattern?)
其中后面跟著“?”的是可選項
例子
1)execution(* *(..))//表示匹配所有方法 2)execution(public * com.savage.service.UserService.*(..))//表示匹配com.savage.server.UserService中所有的公有方法 3)execution(* com.savage.server..*.*(..))//表示匹配com.savage.server包及其子包下的所有方法*> 最靠近(…)的為方法名,靠近.(…))的為類名或者接口名,如上例的JoinPointObjP2.(…))
參考:Spring AOP 中pointcut expression表達式解析及配置
方法參數匹配
args()
@args()
當前AOP代理對象類型匹配
this()
目標類匹配
target()
@target()
within()
@within()
***> 當一個實現了接口的類被AOP的時候,用getBean方法必須cast為接口類型,不能為該類的類型.
標有此注解的方法匹配
@annotation()
//帶有@MyTypeAnnotation標注的所有類的任意方法. @within(com.elong.annotation.MyTypeAnnotation) @target(com.elong.annotation.MyTypeAnnotation) //帶有@MyTypeAnnotation標注的任意方法. @annotation(com.elong.annotation.MyTypeAnnotation)***> @within和@target針對類的注解,@annotation是針對方法的注解
@Before("og()")
這種使用方式等同于以下方式,直接定義execution表達式使用
@Before("execution(* com.savage.aop.MessageSender.*(..))")
Pointcut定義時,還可以使用&&、||、! 這三個運算
@Pointcut("execution(* com.savage.aop.MessageSender.*(..))") private void logSender(){}@Pointcut("execution(* com.savage.aop.MessageReceiver.*(..))") private void logReceiver(){}@Pointcut("logSender() || logReceiver()") private void logMessage(){}還可以將一些公用的Pointcut放到一個類中,以供整個應用程序使用,如下:
package com.savage.aop;import org.aspectj.lang.annotation.*;public class Pointcuts {@Pointcut("execution(* *Message(..))")public void logMessage(){}@Pointcut("execution(* *Attachment(..))")public void logAttachment(){}@Pointcut("execution(* *Service.*(..))")public void auth(){} }在使用上面定義Pointcut時,指定完整的類名加上Pointcut簽名就可以了,如:
package com.savage.aop;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*;@Aspect public class LogBeforeAdvice { @Before("com.sagage.aop.Pointcuts.logMessage()") public void before(JoinPoint joinPoint) { System.out.println("Logging before " + joinPoint.getSignature().getName()); } }也可以使用xml配置
<aop:config><aop:pointcut id="log" expression="execution(* com.savage.simplespring.bean.MessageSender.*(..))"/><aop:aspect id="logging" ref="logBeforeAdvice"><aop:before pointcut-ref="log" method="before"/><aop:after-returning pointcut-ref="log" method="afterReturning"/></aop:aspect> </aop:config> package com.ufgov.util.rest;/*** 記錄api訪問日志的切面*/ @Aspect public class ApiLogAspect{private static Logger logger = Logger.getLogger(ApiLogAspect.class);/*** 定義切入點*/@Pointcut("@annotation(ApiInf)")//這里就是表達式//Pointcut簽名public void controllerAspect() {}@Around("controllerAspect()")public Object around(ProceedingJoinPoint point) throws Throwable {// TODO somethingreturn point.proceed(); // 不調用point.proceed()不會執行目標方法}/*** 進入方法之前處理*/@Before("controllerAspect()")public void doBefore() throws UnsupportedEncodingException{......}/*** 記錄返回信息*/@AfterReturning(pointcut="controllerAspect()",returning="ret")public void doAfterReturn(Object ret) {//對返回數據進行格式化處理,用于入庫}/*** 記錄發生的異常信息* @param e*/@AfterThrowing(value="controllerAspect()",throwing="e")public void doAfterThrow(Throwable e) {e.printStackTrace();......} }在目標方法上添加注解
@ApiInfo ....總結
- 上一篇: 著名作曲家范吉利斯因新冠去世,曾写下震撼
- 下一篇: scrapy爬取斗图表情