Springboot如何正確使用AOP問(wèn)題
?一、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í)行過(guò)程中某個(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是通過(guò)運(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,寫(xiě)在返回值前,任意修飾符填*號(hào)就可以 |
| 返回值類型 | 必選,可以使用*來(lái)代表任意返回值 |
| 方法名 | 必選,可以用*來(lái)代表任意方法 |
| 參數(shù) | ()代表是沒(méi)有參數(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開(kāi)頭的方法
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開(kāi)始攔截, 當(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攔截開(kāi)始進(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開(kāi)始攔截, 當(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攔截開(kāi)始進(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項(xiàng)目使用AOP與自定義注解記錄請(qǐng)求日志方式
- SpringBoot使用AOP切面對(duì)請(qǐng)求進(jìn)行日志記錄方式
- SpringBoot中四種AOP實(shí)戰(zhàn)應(yīng)用場(chǎng)景及代碼實(shí)現(xiàn)
- SpringBoot實(shí)現(xiàn)動(dòng)態(tài)插拔的AOP的完整案例
- SpringBoot中使用AOP切面編程實(shí)現(xiàn)登錄攔截功能
- springboot接口服務(wù),防刷、防止請(qǐng)求攻擊,AOP實(shí)現(xiàn)方式
- Springboot+aop實(shí)現(xiàn)配置多數(shù)據(jù)源的示例代碼
相關(guān)文章
新版SpringBoot無(wú)法主動(dòng)讀取bootstrap.yml的原因和解決方法
在使用新版 Springboot 搭建微服務(wù)時(shí) 發(fā)現(xiàn)配置數(shù)據(jù)源失敗,依賴、配置、注解等所示所有均為正確,所以本文給大家介紹了新版SpringBoot無(wú)法主動(dòng)讀取bootstrap.yml的原因和解決方案,需要的朋友可以參考下2025-01-01
SpringBoot使用Spring-Data-Jpa實(shí)現(xiàn)CRUD操作
這篇文章主要為大家詳細(xì)介紹了SpringBoot使用Spring-Data-Jpa實(shí)現(xiàn)CRUD操作,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08
Springcloud Config支持本地配置文件的方法示例
這篇文章主要介紹了Springcloud Config支持本地配置文件的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
Java 程序里transient關(guān)鍵字使用方法示例
這篇文章主要為大家介紹了Java 程序里transient關(guān)鍵字使用方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
Spring boot 配置多個(gè)redis的方法示例
這篇文章主要介紹了Spring boot 配置多個(gè)redis的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-09-09
淺析RxJava處理復(fù)雜表單驗(yàn)證問(wèn)題的方法
這篇文章主要介紹了RxJava處理復(fù)雜表單驗(yàn)證問(wèn)題的相關(guān)資料,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06
性能調(diào)優(yōu)之java服務(wù)器容器調(diào)優(yōu)詳解
這篇文章主要介紹了java服務(wù)器容器調(diào)優(yōu),如果接口響應(yīng)時(shí)間超過(guò)了既定數(shù)據(jù),項(xiàng)目支撐不了這么大的請(qǐng)求,就需要對(duì)項(xiàng)目以及項(xiàng)目接口進(jìn)行數(shù)據(jù)庫(kù)、容器、緩存等方面的調(diào)優(yōu),文章中有詳細(xì)的代碼示例,需要的朋友可以參考一下2023-04-04
java:無(wú)法訪問(wèn)org.springframework.boot.SpringApplication問(wèn)題
這篇文章主要介紹了java:無(wú)法訪問(wèn)org.springframework.boot.SpringApplication問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08

