Springboot如何正確使用AOP問題
?一、AOP概念
切面(Aspect)
- 一個(gè)關(guān)注點(diǎn)的模塊化,這個(gè)關(guān)注點(diǎn)可能會(huì)橫切多個(gè)對(duì)象。
- 事務(wù)管理是J2EE應(yīng)用中一個(gè)關(guān)于橫切關(guān)注點(diǎn)的很好的例子。
- 在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解的方式來(lái)實(shí)現(xiàn)
連接點(diǎn)(JoinPoint)
在程序執(zhí)行過程中某個(gè)特定的點(diǎn),比如某方法調(diào)用的時(shí)候或者處理異常的時(shí)候。在SpringAOP中,一個(gè)連接點(diǎn)總是表示一個(gè)方法的執(zhí)行
通知(Advice)
- 在切面的某個(gè)特定的連接點(diǎn)上執(zhí)行的動(dòng)作。
- 其中包括了Around、Before和After等不同類型的通知。
- 許多AOP框架(包括Spring)都是以攔截器做通知模型,并維護(hù)一個(gè)以連接點(diǎn)為中心的攔截器鏈
切入點(diǎn)(PointCut)
- 匹配連接點(diǎn)的斷言。
- 通知和一個(gè)切入點(diǎn)表達(dá)式關(guān)聯(lián),并在滿足這個(gè)切入點(diǎn)的連接點(diǎn)上運(yùn)行(例如,當(dāng)執(zhí)行某個(gè)特定名稱的方法時(shí)),切入點(diǎn)表達(dá)式如何和連接點(diǎn)匹配是AOP的核心:Spring缺省使用AspectJ切入點(diǎn)語(yǔ)法
引入(Intorduction)
- 用來(lái)給一個(gè)類型聲明額外的方法或?qū)傩裕ㄒ脖环Q為連接類型聲明)。
- Spring允許引入新的接口(以及一個(gè)對(duì)應(yīng)的實(shí)現(xiàn))到任何被代理的對(duì)象。
- 例如,你可以使用引入來(lái)使一個(gè)bean實(shí)現(xiàn)接口,以便簡(jiǎn)化緩存機(jī)制
目標(biāo)對(duì)象(Target Object)
- 被一個(gè)或者多個(gè)切面所通知的對(duì)象。也被稱做被通知對(duì)象。
- 既然Spring AOP是通過運(yùn)行時(shí)代理實(shí)現(xiàn)的,這個(gè)對(duì)象永遠(yuǎn)是一個(gè)被代理對(duì)象
AOP代理(Aop proxy)
- AOP框架創(chuàng)建的對(duì)象,用來(lái)實(shí)現(xiàn)切面契約(例如通知方法執(zhí)行等等)。
- 在Spring中,AOP代理可以是JDK動(dòng)態(tài)代理或者CGLIB代理
植入(weaving)
- 把切面連接到其它的應(yīng)用程序類型或者對(duì)象上,并創(chuàng)建一個(gè)被通知的對(duì)象。
- 這些可以在編譯時(shí)(例如使用AspectJ編譯器),類加載時(shí)和運(yùn)行時(shí)完成。
- Spring和其他純Java AOP框架一樣,在運(yùn)行時(shí)完成織入
二、切點(diǎn)表達(dá)式?
execution表達(dá)式
用于匹配方法執(zhí)行的連接點(diǎn),屬于方法級(jí)別
語(yǔ)法:
execution(修飾符 返回值類型 方法名(參數(shù))異常)
| 語(yǔ)法參數(shù) | 描述 |
|---|---|
| 修飾符 | 可選,如public,protected,寫在返回值前,任意修飾符填*號(hào)就可以 |
| 返回值類型 | 必選,可以使用*來(lái)代表任意返回值 |
| 方法名 | 必選,可以用*來(lái)代表任意方法 |
| 參數(shù) | ()代表是沒有參數(shù),(..)代表是匹配任意數(shù)量,任意類型的參數(shù),當(dāng)然也可以指定類型的參數(shù)進(jìn)行匹配,如要接受一個(gè)String類型的參數(shù),則(java.lang.String), 任意數(shù)量的String類型參數(shù):(java.lang.String..) |
| 異常 | 可選,語(yǔ)法:throws 異常,異常是完整帶包名,可以是多個(gè),用逗號(hào)分隔 |
符號(hào):
| 符號(hào) | 描述 |
|---|---|
| * | 匹配任意字符 |
| … | 匹配多個(gè)包或者多個(gè)參數(shù) |
| + | 表示類及其子類 |
條件符:
| 符號(hào) | 描述 |
|---|---|
| &&、and | 與 |
| || | 或 |
| ! | 非 |
案例
攔截com.gj.web包下的所有子包里的任意類的任意方法
execution(* com.gj.web..*.*(..))
攔截com.gj.web.api.Test2Controller下的任意方法
execution(* com.gj.web.api.Test2Controller.*(..))
攔截任何修飾符為public的方法
execution(public * * (..))
攔截com.gj.web下的所有子包里的以ok開頭的方法
execution(* com.gj.web..*.ok*(..))
三、AOP通知
在切面類中需要定義切面方法用于響應(yīng)響應(yīng)的目標(biāo)方法,切面方法即為通知方法,通知方法需要用注解標(biāo)識(shí),AspectJ支持5種類型的通知注解
| 注解 | 描述 |
|---|---|
| @Before | 前置通知, 在方法執(zhí)行之前執(zhí)行 |
| @After | 后置通知, 在方法執(zhí)行之后執(zhí)行 |
| @AfterReturn | 返回通知, 在方法返回結(jié)果之后執(zhí)行 |
| @AfterThrowing | 異常通知, 在方法拋出異常之后 |
| @Around | 環(huán)繞通知,圍繞方法的執(zhí)行 |
@Before
@Before("testCut()")
public void cutProcess(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("注解方式AOP開始攔截, 當(dāng)前攔截的方法名: " + method.getName());
}
@After
@After("testCut()")
public void after(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("注解方式AOP執(zhí)行的方法 :"+method.getName()+" 執(zhí)行完了");
}
@AfterReturn:其中value表示切點(diǎn)方法,returning表示返回的結(jié)果放到result這個(gè)變量中
/**
* returning屬性指定連接點(diǎn)方法返回的結(jié)果放置在result變量中
* @param joinPoint 連接點(diǎn)
* @param result 返回結(jié)果
*/
@AfterReturning(value = "testCut()",returning = "result")
public void afterReturn(JoinPoint joinPoint, Object result) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("注解方式AOP攔截的方法執(zhí)行成功, 進(jìn)入返回通知攔截, 方法名為: "+method.getName()+", 返回結(jié)果為: "+result.toString());
}
@AfterThrowing:其中value表示切點(diǎn)方法,throwing表示異常放到e這個(gè)變量
@AfterThrowing(value = "testCut()", throwing = "e")
public void afterThrow(JoinPoint joinPoint, Exception e) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("注解方式AOP進(jìn)入方法異常攔截, 方法名為: " + method.getName() + ", 異常信息為: " + e.getMessage());
}
@Around
@Around("testCut()")
public Object testCutAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("注解方式AOP攔截開始進(jìn)入環(huán)繞通知.......");
Object proceed = joinPoint.proceed();
System.out.println("準(zhǔn)備退出環(huán)繞......");
return proceed;
}
四、springboot中使用AOP
導(dǎo)出依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

自定義注解
package com.hl.springbootrunner.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AOPAnnotations {
}
創(chuàng)建切面類
package com.hl.springbootrunner.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class TestAspect {
/**
* 這里的路徑填自定義注解的全路徑
*/
@Pointcut("@annotation(com.hl.springbootrunner.aop.AOPAnnotations)")
public void testCut() {
}
@Before("testCut()")
public void cutProcess(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("注解方式AOP開始攔截, 當(dāng)前攔截的方法名: " + method.getName());
}
@After("testCut()")
public void after(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("注解方式AOP執(zhí)行的方法 :" + method.getName() + " 執(zhí)行完了");
}
@Around("testCut()")
public Object testCutAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("注解方式AOP攔截開始進(jìn)入環(huán)繞通知.......");
Object proceed = joinPoint.proceed();
System.out.println("準(zhǔn)備退出環(huán)繞......");
return proceed;
}
/**
* returning屬性指定連接點(diǎn)方法返回的結(jié)果放置在result變量中
*
* @param joinPoint 連接點(diǎn)
* @param result 返回結(jié)果
*/
@AfterReturning(value = "testCut()", returning = "result")
public void afterReturn(JoinPoint joinPoint, Object result) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("注解方式AOP攔截的方法執(zhí)行成功, 進(jìn)入返回通知攔截, 方法名為: " + method.getName() + ", 返回結(jié)果為: " + result.toString());
}
@AfterThrowing(value = "testCut()", throwing = "e")
public void afterThrow(JoinPoint joinPoint, Exception e) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("注解方式AOP進(jìn)入方法異常攔截, 方法名為: " + method.getName() + ", 異常信息為: " + e.getMessage());
}
}
自定義一個(gè)接口
@RestController
public class TestController {
@GetMapping("/ok")
@AOPAnnotations
public String test2() {
return "ok";
}
}
測(cè)試

總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringBoot 攔截器 (Interceptor)與切面 (AOP)示例、作用及適用場(chǎng)景分析
- AOP在SpringBoot項(xiàng)目中的使用場(chǎng)景解讀
- SpringBoot整合Jasypt使用自定義注解+AOP實(shí)現(xiàn)敏感字段加解密
- springboot接口服務(wù),防刷、防止請(qǐng)求攻擊,AOP實(shí)現(xiàn)方式
- SpringBoot3利用AOP實(shí)現(xiàn)IP黑名單功能
- springbootAOP定義切點(diǎn)獲取/修改請(qǐng)求參數(shù)方式
- SpringBoot實(shí)現(xiàn)AOP切面的三種方式
- SpringBoot中使用AOP實(shí)現(xiàn)日志記錄功能
- SpringBoot AOP如何配置全局事務(wù)
- JAVA中Spring Boot的AOP切面編程是什么,如何使用?(實(shí)例代碼)
相關(guān)文章
SpringBoot多線程進(jìn)行異步請(qǐng)求的處理方式
這篇文章主要介紹了SpringBoot多線程進(jìn)行異步請(qǐng)求的處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜2021-12-12
SpringMVC Mybatis配置多個(gè)數(shù)據(jù)源并切換代碼詳解
這篇文章主要介紹了SpringMVC Mybatis配置多個(gè)數(shù)據(jù)源并切換代碼詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
JavaWeb項(xiàng)目中DLL文件動(dòng)態(tài)加載方法
在JavaWeb項(xiàng)目中,有時(shí)候我們需要在運(yùn)行時(shí)動(dòng)態(tài)加載DLL文件(在Windows中是DLL,在Linux中是SO文件),這通常用于實(shí)現(xiàn)一些特定的功能,比如調(diào)用本機(jī)代碼或者使用某些特定于操作系統(tǒng)的API,本文將介紹如何在JavaWeb項(xiàng)目中動(dòng)態(tài)加載DLL文件,需要的朋友可以參考下2024-12-12
Java詳解對(duì)象終止方法finalize()的用法
在前面的 jvm 中, 需要補(bǔ)充幾個(gè)部分的內(nèi)容, 接著來(lái)看 finalize() 機(jī)制, 它可以使接近死亡的對(duì)象復(fù)活, 下來(lái)我們來(lái)看是怎么一回事2022-05-05
Java統(tǒng)計(jì)代碼的執(zhí)行時(shí)間的N種方法
在日常開發(fā)中經(jīng)常需要測(cè)試一些代碼的執(zhí)行時(shí)間,但又不想使用向 JMH(Java?Microbenchmark Harness,Java 微基準(zhǔn)測(cè)試套件)這么重的測(cè)試框架,所以本文就匯總了一些 Java 中比較常用的執(zhí)行時(shí)間統(tǒng)計(jì)方法,總共包含以下 6 種,需要的朋友可以參考下2022-08-08
Mybatis使用foreach批量插入Oracle提示命令未正確結(jié)束方式
文章主要討論了在Oracle和MySQL中使用foreach標(biāo)簽時(shí)的語(yǔ)法差異,以及foreach元素的屬性介紹,包括item、index、collection、open、separator和close等2025-11-11
Java 如何將前端傳來(lái)的數(shù)字轉(zhuǎn)化為日期
這篇文章主要介紹了Java 如何將前端傳來(lái)的數(shù)字轉(zhuǎn)化為日期,本文通過示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06

