SpringBoot使用AOP(針對注解)
AOP簡介
- AOP為Aspect Oriented Programming的縮寫,意為:面向切面程式設計,通過預編譯方式和運作期動态代理實作程式功能的統一維護的一種技術
- AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring架構中的一個重要内容,是函數式程式設計的一種衍生範型。
- AOP可以對業務邏輯的各個部分進行隔離,進而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。
- AOP是一個概念,并沒有設定具體語言的實作,它能克服那些隻有單繼承特性語言的缺點,spring2.0之後整合AspectJ第三方AOP技術。
- AspectJ是一個面向切面的架構,它擴充了Java語言。AspectJ定義了AOP文法是以它有一個專門的編譯器用來生成遵守Java位元組編碼規範的Class檔案。
- 主要功能:
- 日志記錄、事務處理、性能統計、安全控制、異常處理、緩存處理、持久化操作、資源池等
- 主要意圖
- 将日志記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來,通過對這些行為的分離,我們希望可以将它們獨立到非指導業務邏輯的方法中,進而改變這些行為的時候不影響業務邏輯的代碼
- AOP與OOP差別
- OOP(面向對象程式設計)針對業務處理過程的實體及其屬性和行為進行抽象封裝,以獲得更加清晰高效的邏輯單元劃分。
- AOP則是針對業務處理過程中的切面進行提取,它所面對的是處理過程中的某個步驟或階段,以獲得邏輯過程中各部分之間低耦合性的隔離效果。這兩種設計思想在目标上有着本質的差異。
- AOP相關術語
- 目标對象target
-
需要被增強的對象,由于spring aop是通過代理模式實作,進而這個對象永遠是被代理對象。
- 連接配接點(join point)
-
指那些被攔截到的點,在spring中這些點指的是方法,因為spring隻支援方法類型的連接配接點,(被增強類中的方法[要不要增強不一定,但是都可以增強])
- 切入點(pointcut)
-
表示一組 joint point,這些 joint point 或是通過邏輯關系組合起來,或是通過通配、正規表達式等方式集中起來,
-
它定義了相應的 Advice 将要發生的地方簡單說切入點是指我們要對哪些連接配接點進行攔截的定義,(已配置,被增強類中的方法[執行增強的連接配接點])
- 通知(advice)
-
指攔截到連接配接點之後所要做的事情就是通知,通知分為前置通知,後置通知,異常通知,最終通知,環繞通知.(增強的代碼)
- 引介introduction
-
引介是一種特殊的通知,在不修改類代碼的前提下,introduction可以在運作期為類動态地添加一些方法或屬性
- 切面aspect
-
是切入點和通知的結合
- 織入weaving
-
織入是一個過程,是将切面應用到目标對象進而建立出AOP代理對象的過程,織入可以在編譯期,類裝載期,運作期進行。
-
Spring采用動态織入,而aspectj采用靜态織入
- 代理Proxy
-
一個類被AOP織入增強後,就産生一個結果代理類
- AOP底層實作
- AOP分為靜态AOP和動态AOP。靜态AOP是指AspectJ實作的AOP,他是将切面代碼直接編譯到Java類檔案中。動态AOP是指将切面代碼進行動态織入實作的AOP。
- Spring的AOP為動态AOP,實作的技術為: JDK提供的動态代理技術 和 CGLIB(動态位元組碼增強技術)
- 傳統的spring aop開發中它支援增強(advice)有五種:
-
- 前置通知 目标方法執行前增強 org.springframework.aop.MethodBeforeAdvice
-
- 後置通知 目标方法執行後增強 org.springframework.aop.AfterReturningAdvice
-
- 環繞通知 目标方法執行前後進行增強 org.aopalliance.intercept.MethodInterceptor
-
- 異常抛出通知 目标方法抛出異常後的增強 org.springframework.aop.ThrowsAdvice
-
- 引介通知 在目标類中添加一些新的方法或屬性
SpringBoot中使用AOP
- 在pom檔案中加入AOP依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
- 建立一個切面類
/**
* @Description
* 其中:
* * @Aspect 表明是一個切面類
* * @Component 将目前類注入到Spring容器内
* * @Pointcut 切入點,其中execution用于使用切面的連接配接點。使用方法:execution(方法修飾符(可選) 傳回類型 方法名 參數 異常模式(可選)) ,可以使用通配符比對字元,*可以比對任意字元。
* * @Before 在方法前執行
* * @After 在方法後執行
* * @AfterReturning 在方法執行後傳回一個結果後執行
* * @AfterThrowing 在方法執行過程中抛出異常的時候執行
* * @Around 環繞通知,就是可以在執行前後都使用,這個方法參數必須為ProceedingJoinPoint,proceed()方法就是被切面的方法,上面四個方法可以使用JoinPoint,JoinPoint包含了類名,被切面的方法名,參數等資訊。
* @Author
* @Version V1.0.0
* @Since 1.0
* @Date 2019-09-25
*/
@Aspect
@Component
public class IndexAspect {
@Pointcut("execution(public * com.mmz.springboot.controller.aop.aopannotation.*.*(..))")
public void LogAspect(){}
@Before("LogAspect()")
public void doBefore(JoinPoint joinPoint){
System.out.println("doBefore");
}
@After("LogAspect()")
public void doAfter(JoinPoint joinPoint){
System.out.println("doAfter");
}
@AfterReturning("LogAspect()")
public void doAfterReturning(JoinPoint joinPoint){
System.out.println("doAfterReturning");
}
@AfterThrowing("LogAspect()")
public void deAfterThrowing(JoinPoint joinPoint){
System.out.println("deAfterThrowing");
}
@Around("LogAspect()")
public Object deAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("deAround");
return joinPoint.proceed();
}
}
- 利用自定義注解使用AOP
/**
* 建立自定義注解,建立注解與建立接口類似,将interface改為@interface即可。
*/
@Target({ElementType.METHOD, ElementType.TYPE}) //定義注解修飾的目标,方法/類
@Retention(RetentionPolicy.RUNTIME) //定義注解的生命周期(SOURCE源碼級别,CLASS編譯期級别,RUNTIME運作期級别)
public @interface DoneTime{
String param() default "";
}
- 建立自定義注解對應切面(在使用注解的地方,利用環繞通知可做進一步的邏輯處理 )
/**
* @Description 建立自定義注解對應切面
* @Author zhoumm
* @Version V1.0.0
* @Since 1.0
* @Date 2019-09-25
*/
@Aspect
@Component
public class DoneTimeAspect {
@Around("@annotation(doneTime)") //對指定注解使用環繞通知
public Object around(ProceedingJoinPoint joinPoint, DoneTime doneTime) throws Throwable {
System.out.println("方法開始時間是:"+new Date());
Object o = joinPoint.proceed();
System.out.println("方法結束時間是:"+new Date()) ;
return o;
}
}
- 建立一個IndexController進行測試 (兩個接口進行效果對比)
/**
* @Description
* @Author
* @Version V1.0.0
* @Since 1.0
* @Date 2019-09-25
*/
@Api(value = "aop", description = "aop測試樣例文檔")
@RestController
@RequestMapping("/aop")
@Slf4j
@Validated
public class IndexController {
@GetMapping("/index")
@DoneTime(param = "IndexController")
public String index(){
System.out.println("方法執行");
return "hello guodegang";
}
@GetMapping("/index2")
public String index2(){
System.out.println("方法2執行");
return "hello yuqian";
}
}
- 浏覽器通路 http://localhost:8787/aop/index1,控制台如下:
方法開始時間是:Wed Sep 25 17:16:02 CST 2019
deAround
doBefore
方法執行
doAfter
doAfterReturning
方法結束時間是:Wed Sep 25 17:16:02 CST 2019
- 浏覽器通路 http://localhost:8787/aop/index2,控制台如下:
deAround
doBefore
方法2執行
doAfter
doAfterReturning