一、aop介紹
AOP的原理是面向切面編程,通過預(yù)編譯方式和運行期動態(tài)代理實現(xiàn)程序功能的統(tǒng)一維護的一種技術(shù),是一種動態(tài)的抽象;主要使用場景是事務(wù)管理、日志打印、監(jiān)控、統(tǒng)一封裝接口返回對象;
以Spring Boot項目為例(Maven),使用AOP需要引入jar包:
<!-- 引入aop支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
二、Aspect
2.1 Aspect的原理及使用場景
原理:通過定義切點,然后在代碼編譯階段或者運行階段對該切點進行邏輯增強(前置增強、后置增強、環(huán)繞增強等);
一般可適用的場景有:
日志邏輯:統(tǒng)一對切點方法出/入?yún)⑦M行打印;
異常處理邏輯:捕獲切點方法的異常信息,進行異常數(shù)據(jù)封裝返回;
出入?yún)⑦壿?/strong>:對入?yún)⑦M行解密,對出參進行加密;對入?yún)⑦M行格式解析;
報警處理邏輯:對切點方法的特定返回結(jié)果或異常,進行攔截報警、用戶觸達等特定操作;
前置增強:執(zhí)行指定切點方法之前,先執(zhí)行增強邏輯;
后置增強:執(zhí)行指定切點方法之后,再執(zhí)行增強邏輯;
環(huán)繞增強:可以再執(zhí)行指定切點方法之前或之后,分別執(zhí)行不同的增強邏輯。
2.2 Aspect的具體代碼示例及說明
@Component
@Aspect
public class DemoAspect {
private static Logger logger = (Logger) LoggerFactory.getLogger(DemoAspect.class);
/**
* 本項目中事例實現(xiàn)的是對請求Controller接口的調(diào)用的增強(環(huán)繞增強)
* 增加了入?yún)r截打印、出參攔截打印、異常的捕獲處理等;
*/
/**
* 增強點/切點
*/
@Pointcut("execution(* *.*.*.controller.*.*(..))")
private void pointFun(){
logger.info("執(zhí)行切入點的邏輯...");
}
/**
* 環(huán)繞增強
* @param joinPoint
*/
@Around("pointFun()")
private Object aroundAspect(ProceedingJoinPoint joinPoint){
logger.info("進入環(huán)繞通知~");
Object resultObj = null;
Object[] args = joinPoint.getArgs();
try {
logger.info("方法執(zhí)行前處理,相當于Before..");
logger.info("方法入?yún)rgs:" + JsonUtil.convertToString(args));
logger.info("開始執(zhí)行原方法:");
resultObj = joinPoint.proceed(args);
logger.info("原方法執(zhí)行完畢。");
logger.info("方法返回值resultObj:" +resultObj);
logger.info("方法執(zhí)行后處理,相當于After..");
} catch (Exception e) {
logger.info("方法異常處理...");
//TODO 異常信息報送到預(yù)警(報警)平臺;
} catch (Throwable e) {
logger.info("方法異常處理...");
//TODO 異常信息報送到預(yù)警(報警)平臺;
} finally {
logger.info("方法最終處理。finally.");
}
return resultObj;
}
}
說明:
1、DemoAspect 類增加@Aspect注解說明,表明該類為AOP切面類;
2、DemoAspect()方法增加@Pointcut注解并引入execution表達式,表明針對controller包下的所有類和方法都需要增強;
3、該案例中使用的是環(huán)繞增強,還有其他方式的增強請參考下方的注解說明:
切面(Aspect):一般是指被@Aspect修飾的類,代表著某一具體功能的AOP邏輯。
切入點(Pointcut):選擇哪些增強的方法,上述體現(xiàn)的是@pointcut注解和execution表達式
通知(Advice):對目標方法的增強
>>>環(huán)繞通知(@Around):內(nèi)部執(zhí)行連接點(方法),對其進行增強
>>>前置通知(@Before):在執(zhí)行連接點前執(zhí)行
>>>后置通知(@After):在執(zhí)行連接點后執(zhí)行
>>>返回通知(@AfterReturning):在連接點返回后執(zhí)行
>>>異常通知(@AfterThrowing):在連接點爆出異常后執(zhí)行
連接點(JoinPoint):就是那些被切入點選中的方法
execution表達式: 看圖講解
說明:execution配置說明.png
訪問修飾符、異常類型可以省略,其余都是必填的;
方法參數(shù),.. 代表所有參數(shù);
類路徑中,.. 代表多層路徑,包括當前包的類和子包的類;
2.3 代碼測試
@RestController
@RequestMapping("/demoController")
public class DemoForAspectController {
/**
* 沒有返回值 void
*/
@RequestMapping("/getVoid")
public void getVoid(){
System.out.println("getVoid");
}
/**
* 返回bean對象
* @return
*/
@RequestMapping("/getObj")
public DemoBean getObj(){
System.out.println("getObj");
DemoBean bean = new DemoBean();
bean.setId(1);
bean.setName("虎子");
bean.setBirthday(new Date());
return bean;
}
/**
* 返回bean對象
* @return
*/
@RequestMapping("/getObjByParam")
public DemoBean getObjByParam(String name){
System.out.println("getObjByParam");
DemoBean bean = new DemoBean();
bean.setId(2);
bean.setName(name);
bean.setBirthday(new Date());
return bean;
}
/**
* 返回Result對象,不用再ResponseAdvice轉(zhuǎn)換
* @return
*/
@RequestMapping("/getResult")
public Result getResult(){
System.out.println("getResult");
return Result.failed();
}
/**
* 拋異常的方法--> DemoAspect可以觸發(fā)到try..catch的異常捕獲里面;
* @return
*/
@RequestMapping("/getException")
public String getException(){
if(true){
throw new RuntimeException("這是錯誤描述");
}
return "返回數(shù)據(jù)";
}
}
使用Postman測試工具請求:http://localhost:8080/projectName/demoController/getObjByParam?name=小虎子
日志打印如下:
2023-07-12 10:42:02.037 INFO 48260 --- [nio-8080-exec-1] c.e.w.service.aop.aspect.DemoAspect : 進入環(huán)繞通知~
2023-07-12 10:42:02.037 INFO 48260 --- [nio-8080-exec-1] c.e.w.service.aop.aspect.DemoAspect : 方法執(zhí)行前處理,相當于Before..
2023-07-12 10:42:02.056 INFO 48260 --- [nio-8080-exec-1] c.e.w.service.aop.aspect.DemoAspect : 方法入?yún)rgs:["小虎子"]
2023-07-12 10:42:02.056 INFO 48260 --- [nio-8080-exec-1] c.e.w.service.aop.aspect.DemoAspect : 開始執(zhí)行原方法:
getObjByParam
2023-07-12 10:42:02.060 INFO 48260 --- [nio-8080-exec-1] c.e.w.service.aop.aspect.DemoAspect : 原方法執(zhí)行完畢。
2023-07-12 10:42:02.090 INFO 48260 --- [nio-8080-exec-1] c.e.w.service.aop.aspect.DemoAspect : 方法返回值resultObj:{"id":1,"name":"小虎子","birthday":1689129722060}
2023-07-12 10:42:02.090 INFO 48260 --- [nio-8080-exec-1] c.e.w.service.aop.aspect.DemoAspect : 方法執(zhí)行后處理,相當于After..
2023-07-12 10:42:02.090 INFO 48260 --- [nio-8080-exec-1] c.e.w.service.aop.aspect.DemoAspect : 方法最終處理。finally.
使用Postman測試工具請求:http://localhost:8080/projectName/demoController/getException
日志打印如下:
2023-07-12 10:34:46.355 INFO 40484 --- [nio-8080-exec-1] c.e.w.service.aop.aspect.DemoAspect : 進入環(huán)繞通知~
2023-07-12 10:34:46.355 INFO 40484 --- [nio-8080-exec-1] c.e.w.service.aop.aspect.DemoAspect : 方法執(zhí)行前處理,相當于Before..
2023-07-12 10:34:46.356 INFO 40484 --- [nio-8080-exec-1] c.e.w.service.aop.aspect.DemoAspect : 方法入?yún)rgs:[]
2023-07-12 10:34:46.356 INFO 40484 --- [nio-8080-exec-1] c.e.w.service.aop.aspect.DemoAspect : 開始執(zhí)行原方法:
2023-07-12 10:34:46.356 INFO 40484 --- [nio-8080-exec-1] c.e.w.service.aop.aspect.DemoAspect : 方法異常處理...
2023-07-12 10:34:46.356 INFO 40484 --- [nio-8080-exec-1] c.e.w.service.aop.aspect.DemoAspect : 方法最終處理。finally.
