前言
本篇文章主要介紹的是SpringBoot切面Aop的demo簡單講解。
SpringBoot Aop
說明:如果想直接獲取工程那么可以直接跳到底部,通過鏈接下載工程代碼。
切面(Aop)
一、概念
AOP(Aspect OrientedProgramming):面向切面編程,面向切面編程(也叫面向方面編程),是目前軟件開發中的一個熱點,也是Spring框架中的一個重要內容。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
二、用途
日志記錄,性能統計,安全控制,權限管理,事務處理,異常處理,資源池管理。
三、詳解
1.切面(Aspect):
官方的抽象定義為“一個關注點的模塊化,這個關注點可能會橫切多個對象”,在本例中,“切面”就是類TestAspect所關注的具體行為,例如:AServiceImpl.barA()的調用就是切面TestAspect所關注的行為之一。“切面”在ApplicationContext中aop:aspect來配置。
2.連接點(Joinpoint):
程序執行過程中的某一行為,例如,AServiceImpl.barA()的調用或者BServiceImpl.barB(String _msg, int _type)拋出異常等行為。
3.通知(Advice):
“切面”對於某個“連接點”所產生的動作,例如,TestAspect中對com.spring.service包下所有類的方法進行日志記錄的動作就是一個Advice。其中,一個“切面”可以包含多個“Advice”,例如TestAspect。Advice共有如下5種類型:
- A 前置通知(Before advice) :在某連接點(JoinPoint)之前執行的通知,但這個通知不能阻止連接點前的執行。xml中在aop:aspect里面使用aop:before元素進行聲明;例如,TestAspect中的doBefore方法。注解中使用@Before聲明;例如,TestAnnotationAspect中的doBefore方法。
- B 后通知(After advice):當某連接點退出的時候執行的通知(不論是正常返回還是異常退出)。xml中在aop:aspect里面使用aop:after元素進行聲明。例如,TestAspect中的doAfter方法,所以AOPTest中調用BServiceImpl.barB拋出異常時,doAfter方法仍然執行。注解中使用@After聲明。
- C 返回后通知(After return advice):在某連接點正常完成后執行的通知,不包括拋出異常的情況。xml中在aop:aspect里面使用
元素進行聲明。注解中使用@AfterReturning聲明; - D 環繞通知(Around advice) :包圍一個連接點的通知,類似Web中Servlet規范中的Filter的doFilter方法。可以在方法的調用前后完成自定義的行為,也可以選擇不執行。xml中在aop:aspect里面使用aop:around元素進行聲明。例如,TestAspect中的doAround方法。注解中使用@Around聲明。
- E 拋出異常后通知(After throwing advice) : 在方法拋出異常退出時執行的通知。xml中在aop:aspect里面使用aop:after-throwing元素進行聲明。例如,TestAspect中的doThrowing方法。注解中使用@AfterThrowing聲明。
- 通知執行順序:前置通知→環繞通知連接點之前→連接點執行→環繞通知連接點之后→返回通知→后通知 →(如果發生異常)異常通知→后通知。
4.切入點(Pointcut)
匹配連接點的斷言,在AOP中通知和一個切入點表達式關聯。例如,TestAspect中的所有通知所關注的連接點,都由切入點表達式execution(* com.spring.service..(..))來決定。
注:以上的理論知識參考:https://www.cnblogs.com/yepei/p/4735298.html
開發准備
環境要求
JDK:1.8
SpringBoot:2.2.6.RELEASE
首先還是Maven的相關依賴,基本和普通springboot項目一樣,就是多了spring-boot-starter-aop
這jar的依賴。
pom.xml文件如下:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>test</scope>
</dependency>
</dependencies>
application.properties
的文件的配置:
banner.charset=UTF-8
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
spring.messages.encoding=UTF-8
spring.application.name=springboot-aspect
server.port=8180
代碼編寫
SpringBoot在使用切面的時候,只需要在自定義的一個切面類中加上 @Aspect
注解進行聲明,然后在自定義切面的類方法中加上對應的注解即可。
比如這里的示例我們想做一個請求響應的加解密切面處理,業務層只需關心代碼邏輯實現,而不用關心請求參數和響應參數的加解密實現。那么首先我們需要自定義一個加解密的切面類,在該類添加@Aspect
注解,然后在定義一個公共的切入點(Pointcut),指向需要處理的包,然后在定義一個前置通知(添加@Before
注解)和后置通知(添加@AfterReturning
)方法實現即可。
這里我們就將切入點設置為控制層包里所有的請求。
切入點代碼示例:
@Pointcut("execution(public * com.pancm.web.*.*(..))")
public void doOperation() {
}
然后在定義一個前置通知,實現對請求參數的數據解密,這里我們就用User這個實體類的名稱,對該數據進行解密。實際在運用是可以根據自身的情況來編寫。
前置通知代碼示例:
@Before("doOperation()")
public void before(JoinPoint joinPoint) throws Throwable{
Object[] objs = joinPoint.getArgs();
for (Object obj : objs) {
User user =(User) obj;
System.out.println("前置通知接受的參數:"+user);
String name =base64DeStr(user.getName());
user.setName(name);
}
}
在編寫完前置通知的方法之后,我們在編寫后置通知的代碼,這塊基本和前置通知的一樣,就是把返回的數據進行加密而已。
后置通知代碼示例:
@AfterReturning(returning = "object", pointcut = "doOperation()")
public void doAfterReturning(Object object) {
ResultBody resultBody = (ResultBody) object;
String str =null;
try {
str=base64EnStr(resultBody.getResult());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
resultBody.setResult(str);
System.out.println("后通知響應的參數:"+resultBody);
}
完整代碼示例:
@Aspect
@Component
public class ParamAspect {
@Pointcut("execution(public * com.pancm.web.*.*(..))")
public void doOperation() {
}
@Before("doOperation()")
public void before(JoinPoint joinPoint) throws Throwable{
Object[] objs = joinPoint.getArgs();
for (Object obj : objs) {
User user =(User) obj;
System.out.println("前置通知接受的參數:"+user);
String name =base64DeStr(user.getName());
user.setName(name);
}
}
@AfterReturning(returning = "object", pointcut = "doOperation()")
public void doAfterReturning(Object object) {
ResultBody resultBody = (ResultBody) object;
String str =null;
try {
str=base64EnStr(resultBody.getResult());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
resultBody.setResult(str);
System.out.println("前置通知響應的參數:"+resultBody);
}
public String base64EnStr(String str) throws UnsupportedEncodingException {
return Base64.getEncoder().encodeToString(str.getBytes("UTF-8"));
}
public static String base64DeStr(String encodeStr) throws UnsupportedEncodingException {
byte[] decodeStr = Base64.getDecoder().decode(encodeStr);
return new String(decodeStr, "UTF-8");
}
實體類代碼
public class User {
private Long id;
private String name;
private Integer age;
//getter 和 setter 略
}
控制層z這塊的代碼和普通的一樣,我們這里只需簡單的做下處理即可,這里為了方便理解,沒有編寫service層和dao的代碼。:
** 控制層代碼**
@RestController
@RequestMapping(value = "/api")
public class UserRestController {
@GetMapping("/user")
public ResultBody findByUser(User user) {
System.out.println("用戶查詢接口請求的參數:"+user);
ResultBody resultBody = new ResultBody();
List<User> userList =new ArrayList<>();
User user2=new User();
user2.setId(1L);
user2.setName("xuwujing");
user2.setAge(18);
userList.add(user2);
resultBody.setCode("0");
resultBody.setResult(userList.toString());
System.out.println("用戶查詢接口響應的參數:"+resultBody);
return resultBody;
}
}
App 入口
和普通的SpringBoot項目基本一樣!
代碼如下:
@SpringBootApplication
public class AspectApp
{
public static void main( String[] args )
{
SpringApplication.run(AspectApp.class, args);
System.out.println("Aspect啟動成功!");
}
}
功能測試
編寫完代碼之后,我們啟動程序,因為是Get請求,在瀏覽器或者使用Postman輸入地址都可以進行測試,需要注意的是這里我們需要對name的值進行base64加密請求。
輸入:
控制台打印:
前置通知接受的參數:{"name":"eHV3dWppbmc="}
用戶查詢接口請求的參數:{"name":"xuwujing"}
用戶查詢接口響應的參數:{"code":"0","result":"[{"age":18,"id":1,"name":"xuwujing"}]"}
后通知響應的參數:{"code":"0","result":"W3siYWdlIjoxOCwiaWQiOjEsIm5hbWUiOiJ4dXd1amluZyJ9XQ=="}
請求響應參數:
{
"code": "0",
"message": null,
"result": "W3siYWdlIjoxOCwiaWQiOjEsIm5hbWUiOiJ4dXd1amluZyJ9XQ=="
}
示例圖:
其它
關於SpringBoot切面Aop的demo簡單講解的文章就講解到這里了,如有不妥,歡迎指正!
項目地址
SpringBoot 的aop的項目工程地址:
https://github.com/xuwujing/springBoot-study/tree/master/springboot-aspect
SpringBoot整個集合的地址:
https://github.com/xuwujing/springBoot-study
SpringBoot整合系列的文章
音樂推薦
翩若驚鴻,婉若游龍,榮曜秋菊,華茂春松。仿佛兮若輕雲之蔽月,飄飄兮若流風之回雪。遠而望之,皎若太陽升朝霞;迫而察之,灼若芙蕖出淥波。--網易雲網友評論
原創不易,如果感覺不錯,希望給個推薦!您的支持是我寫作的最大動力!
版權聲明:
作者:虛無境
博客園出處:http://www.cnblogs.com/xuwujing
CSDN出處:http://blog.csdn.net/qazwsxpcm
個人博客出處:http://www.panchengming.com