AOP
AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。在日常開發當中經常用來記錄日誌,方法跟蹤、事務,權限等
切面方法說明:
- @Aspect -- 作用是把當前類標識爲一個切面供容器讀取
- @Pointcut -- (切入點):就是帶有通知的連接點,在程序中主要體現爲書寫切入點表達式
- @Before -- 標識一個前置增強方法,相當於BeforeAdvice的功能
- @AfterReturning -- 後置增強,相當於AfterReturningAdvice,方法退出時執行
- @AfterThrowing -- 異常拋出增強,相當於ThrowsAdvice
- @After -- final增強,不管是拋出異常或者正常退出都會執行
- @Around -- 環繞增強,相當於MethodInterceptor
引入AOP依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
定義一個切面--TestAspect
@Aspect
@Component
@Slf4j
public class TestAspect {
//com.kzj.kzj_rabbitmq.controller 包中所有的類的所有方法切面
//@Pointcut("execution(public com.kzj.kzj_rabbitmq.controller..(..))")
//只針對 MessageController 類切面
//@Pointcut("execution(public com.kzj.kzj_rabbitmq.controller.MessageController.(..))")
//統一切點,對com.kzj.kzj_rabbitmq.controller及其子包中所有的類的所有方法切面
@Pointcut("execution(public com.kzj.kzj_rabbitmq.controller...(..))")
public void Pointcut() {
}
//前置通知
@Before("Pointcut()")
public void beforeMethod(JoinPoint joinPoint){
log.info("調用了前置通知");
}
//@After: 後置通知
@After("Pointcut()")
public void afterMethod(JoinPoint joinPoint){
log.info("調用了後置通知");
}
//@AfterRunning: 返回通知 rsult爲返回內容
@AfterReturning(value="Pointcut()",returning="result")
public void afterReturningMethod(JoinPoint joinPoint,Object result){
log.info("調用了返回通知");
}
//@AfterThrowing: 異常通知
@AfterThrowing(value="Pointcut()",throwing="e")
public void afterReturningMethod(JoinPoint joinPoint, Exception e){
log.info("調用了異常通知");
}
//@Around:環繞通知
@Around("Pointcut()")
public Object Around(ProceedingJoinPoint pjp) throws Throwable {
log.info("around執行方法之前");
Object object = pjp.proceed();
log.info("around執行方法之後--返回值:" +object);
return object;
}
}
添加測試用Controller
@RestController
@Slf4j
public class MessageController {
@RequestMapping(value="/send_message",produces = "text/json;charset=UTF-8")
public String send_message(MessagePojo pojo) throws Exception {
log.info("執行了controller.send_message方法");
return JSON.toJSONString(pojo);
}
}
測試:
在瀏覽器輸入:http://localhost:9999/send_message?delay=15&className=B</a></p>
可看到打印
可以看到,aspect類內部的 advice 將按照以下的順序進行執行
下面是項目中實戰-使用AOP打印日誌和效率監聽(記錄請求參數和返回結果和方法運行總時間)
@Aspect
@Component
@Slf4j
public class TestAspect {
//com.kzj.kzj_rabbitmq.controller 包中所有的類的所有方法切面
//@Pointcut("execution(public com.kzj.kzj_rabbitmq.controller..(..))")
//只針對 MessageController 類切面
//@Pointcut("execution(public com.kzj.kzj_rabbitmq.controller.MessageController.(..))")
//統一切點,對com.kzj.kzj_rabbitmq.controller及其子包中所有的類的所有方法切面
@Pointcut("execution(public com.kzj.kzj_rabbitmq.controller...(..))")
public void Pointcut() {
}
//@Around:環繞通知
@Around("Pointcut()")
public Object Around(ProceedingJoinPoint pjp) throws Throwable {
Map<String,Object> data = new HashMap<>();
//獲取目標類名稱
String clazzName = pjp.getTarget().getClass().getName();
//獲取目標類方法名稱
String methodName = pjp.getSignature().getName();
//記錄類名稱
data.put("clazzName",clazzName);
//記錄對應方法名稱
data.put("methodName",methodName);
//記錄請求參數
data.put("params",pjp.getArgs());
//開始調用時間
// 計時並調用目標函數
long start = System.currentTimeMillis();
Object result = pjp.proceed();
Long time = System.currentTimeMillis() - start;
//記錄返回參數
data.put("result",result);
//設置消耗總時間
data.put("consumeTime",time);
System.out.println(data);
return result;
}
}