springboot使用自定義注解實(shí)現(xiàn)aop切面日志
平時我們在開發(fā)過程中,代碼出現(xiàn)bug時為了更好的在服務(wù)器日志中尋找問題根源,會在接口的首尾打印日志,看下參數(shù)和返回值是否有問題。但是手動的logger.info() 去編寫時工作量較大,這時我們可以使用AOP切面,為所有接口的首尾打印日志。
實(shí)現(xiàn)AOP切面日志一般有兩種方式:
1、攔截所有接口controller,在首尾打印日志
2、攔截指定注解的接口,為有該注解的接口首尾打印日志
我們嘗試用自定義注解來實(shí)現(xiàn)AOP日志的打印,這樣擁有更高的靈活性。廢話不多說,我們開始
1. 導(dǎo)入切面需要的依賴包
<dependency> ? ? ? <groupId>org.springframework.boot</groupId> ? ? ?<artifactId>spring-boot-starter-aop</artifactId> </dependency>
2. 自定義注解 AOPLog , 指定注解使用在方法上, 指定在運(yùn)行時有效
Target:描述了注解修飾的對象范圍
- METHOD:用于描述方法
- PACKAGE:用于描述包
- PARAMETER:用于描述方法變量
- TYPE:用于描述類、接口或enum類型
Retention: 表示注解保留時間長短
- SOURCE:在源文件中有效,編譯過程中會被忽略
- CLASS:隨源文件一起編譯在class文件中,運(yùn)行時忽略
- RUNTIME:在運(yùn)行時有效
只有定義為 RetentionPolicy.RUNTIME(在運(yùn)行時有效)時,我們才能通過反射獲取到注解,然后根據(jù)注解的一系列值,變更不同的操作。
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; ? /** ?* @author buer ?* @date 2019/12/26 ?*/ // 指定注解使用在方法上 @Target(ElementType.METHOD) // 指定生效至運(yùn)行時 @Retention(RetentionPolicy.RUNTIME) public @interface AOPLog { ? ? ? /** ? ? ?* 指定是否詳情顯示 ? ? ?* true 顯示詳情, 默認(rèn)false ? ? ?* ? ? ?* @return ? ? ?*/ ? ? boolean isDetail() default false; ? }
3. 設(shè)置切面類
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; ? import java.lang.reflect.Method; ? /** ?* @author buer ?* @date 2019/12/26 ?* @description //TODO ?*/ // 指定切面類 @Aspect // 注入容器 @Component public class AOPLogAspect { ? ? ? private static Logger log = LoggerFactory.getLogger(AOPLogAspect.class); ? ? ? /** ? ? ?* 指定切點(diǎn), 切點(diǎn)的位置是存在該注解com.xingyun.xybb.demo.annotation.AOPLog ? ? ?*/ ? ? @Pointcut("@annotation(com.xingyun.xybb.demo.annotation.AOPLog)") ? ? public void logPointCut() { ? ? } ? ? ? /** ? ? ?* 環(huán)繞通知, 該處寫具體日志邏輯 ? ? ?* ? ? ?* @param joinPoint ? ? ?*/ ? ? @Around("logPointCut()") ? ? public void logAround(ProceedingJoinPoint joinPoint) { ? ? ? ? MethodSignature signature = (MethodSignature) joinPoint.getSignature(); ? ? ? ? // 獲取方法名稱 ? ? ? ? String methodName = signature.getName(); ? ? ? ? // 獲取入?yún)? ? ? ? ? Object[] param = joinPoint.getArgs(); ? ? ? ? StringBuilder sb = new StringBuilder(); ? ? ? ? for (Object o : param) { ? ? ? ? ? ? sb.append(o).append("; "); ? ? ? ? } ? ? ? ? log.info("進(jìn)入方法[{}], 參數(shù)有[{}]", methodName, sb.toString()); ? ? ? ? ? String resp = ""; ? ? ? ? try { ? ? ? ? ? ? Object proceed = joinPoint.proceed(); ? ? ? ? ? ? resp = JSON.toJSONString(proceed, SerializerFeature.WriteMapNullValue); ? ? ? ? } catch (Throwable throwable) { ? ? ? ? ? ? throwable.printStackTrace(); ? ? ? ? } ? ? ? ? ? // 獲取方法上的注解,判斷如果isDetail值為true,則打印結(jié)束日志 ? ? ? ? Method method = signature.getMethod(); ? ? ? ? AOPLog annotation = method.getAnnotation(AOPLog.class); ? ? ? ? boolean isDetail = annotation.isDetail(); ? ? ? ? if (isDetail) { ? ? ? ? ? ? log.info("方法[{}]執(zhí)行結(jié)束, 返回值[{}]", methodName, resp); ? ? ? ? } ? ? } ? }
4. 編寫測試接口, 測試切面日志是否生效
import com.xingyun.xybb.common.response.XyResponseEntity; import com.xingyun.xybb.demo.annotation.AOPLog; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; ? /** ?* @author buer ?* @date 2019/12/26 ?* @description //TODO ?*/ @RestController public class TestAOPLogController { ? ?? ? ? // 指定注解@AOPLog ? ? @AOPLog ? ? @GetMapping("/testAOP") ? ? public ResponseEntity<?> testAOPLog() { ? ? ? ? return XyResponseEntity.ok(); ? ? } ? ? ? // 指定注解@AOPLog, 同時isDetail = true ? ? @AOPLog(isDetail = true) ? ? @GetMapping("/testAOPLogDetail") ? ? public ResponseEntity<?> testAOPLogDetail() { ? ? ? ? return XyResponseEntity.ok(); ? ? } ? }
5. 分別請求兩測試接口
http://localhost:8499/demo/testAOP
http://localhost:8499/demo/testAOPLogDetail
控制臺打印出
2019-12-26 14:00:56.336 ***.AOPLogAspect : 進(jìn)入方法[testAOPLog], 參數(shù)有[]
2019-12-26 14:01:00.372 ***.AOPLogAspect : 進(jìn)入方法[testAOPLogDetail], 參數(shù)有[]
2019-12-26 14:01:00.373 ***.AOPLogAspect : 方法[testAOPLogDetail]執(zhí)行結(jié)束, 返回值[{"body":{"retCode":200,"retEntity":null,"retMsg":"OK"},"headers":{},"statusCode":"OK","statusCodeValue":200}]
由此可看出,AOP切面攔截成功,打印出了日志,同時設(shè)置了 isDetail = true 時,打印出了結(jié)束日志。
自定義注解實(shí)現(xiàn)AOP切面打印日志完成。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java中String類getBytes()方法詳解與完整實(shí)例
這篇文章主要給大家介紹了關(guān)于Java中String類getBytes()方法詳解與完整實(shí)例的相關(guān)資料,getBytes()是Java編程語言中將一個字符串轉(zhuǎn)化為一個字節(jié)數(shù)組byte[]的方法,需要的朋友可以參考下2023-10-10java實(shí)現(xiàn)excel和txt文件互轉(zhuǎn)
本篇文章主要介紹了java實(shí)現(xiàn)excel和txt文件互轉(zhuǎn)的相關(guān)知識。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04本地安裝MinIO分布式對象存儲服務(wù)器的詳細(xì)步驟
本地安裝MinIO非常簡單,MinIO提供了獨(dú)立的二進(jìn)制文件,無需額外的依賴,本文介紹如何在本地安裝MinIO分布式對象存儲服務(wù)器,感興趣的朋友一起看看吧2024-01-01java json字符串轉(zhuǎn)JSONObject和JSONArray以及取值的實(shí)例
這篇文章主要介紹了java json字符串轉(zhuǎn)JSONObject和JSONArray以及取值的實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-05-05Spring Boot2發(fā)布調(diào)用REST服務(wù)實(shí)現(xiàn)方法
這篇文章主要介紹了Spring Boot2發(fā)布調(diào)用REST服務(wù)實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-04-04