六、集成全局AOP切面,進行訪問時間與日誌打印
- 2019 年 11 月 7 日
- 筆記
本次開發環境為:
系統:Windows 10 10.0
JDK:JRE: 1.8.0_152-release-1136-b43 amd64 JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
開發工具:IntelliJ IDEA 2018.1.8
springboot框架:2.2.0
1、在pom.xml中增加包引用
<!-- Springboot的AOP包 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
2、新建一個controller包用於本次測試使用
@RestController public class HelloWorldAOPController { private Logger log = LoggerFactory.getLogger(HelloWorldAOPController.class); /** * hello請求測試 實例:http://localhost:8081/hello?name=world * @param name 打招呼的姓名 * @return 返回打招呼的整體語句 */ @RequestMapping("/hello") public String hello(String name){ StringBuffer hellos = new StringBuffer(); hellos.append("Hello "); hellos.append(name); return hellos.toString(); } }
3、新建一個WebTimeAspect類,用於記錄日誌切面,這裡需要注意截止記錄是在retrun後的切入點,因其是對全局controller進行的切面,因此使用@AfterReturning更合適的一些,如果我們所有均切入則使用@After更合適一些。
@Aspect @Component @Order(1) public class WebTimeAspect { private Logger log = LoggerFactory.getLogger(WebTimeAspect.class); /** * 聲明一個線程,用於記錄請求與響應整個周期期間在服務端消耗的時間 */ private ThreadLocal<Long> startTime = new ThreadLocal<>(); /** * 在請求響應之前,即請求到達當前服務端 * 所有demo3下的controller均經過該切面 * @param joinPoint */ @Before("within(com.cnhuashao.rapiddevelopment.core..*.*)") public void doBefore(JoinPoint joinPoint){ log.info("----------- WebTimeAspect doBefore -----------------------------------------"); startTime.set(System.currentTimeMillis()); } /** * 在請求響應之後,即請求已經經過controller處理返回後 * @param rvt */ @AfterReturning(value = "within(com.cnhuashao.rapiddevelopment.core..*.*)",returning = "rvt") public void doAfterReturning(Object rvt){ log.info("-----------Start WebTimeAspect doAfterReturning ------"); log.info("本次處理請求耗費時間 : {}",(System.currentTimeMillis() - startTime.get())); log.info("-----------End WebTimeAspect doAfterReturning -------------------------------"); } }
4、進行測試請求響應處理時間
訪問地址:http://localhost:8081/hello?name=cnHuaShao

image.png
5、新建一個WebLogAspect類,用於請求日誌與響應日誌的記錄
@Aspect @Component @Order(2) public class WebLogAspect { private Logger log = LoggerFactory.getLogger(WebLogAspect.class); /** * 訪問localhost時打印的IP地址 */ private static final String IP_LOCALHOST ="0:0:0:0:0:0:0:1"; /** * com.cnhuashao.rapiddevelopment.core包及所有子包下任何類的任何方法 */ @Pointcut("execution(* com.cnhuashao.rapiddevelopment.core..*.*(..))") public void webLog(){ } /** * 在請求響應之前,即請求到達當前服務端 * 所有demo3下的controller均經過該切面 * @param joinPoint */ @Before("webLog()") public void doBefore(JoinPoint joinPoint){ log.info(" ---------- WebLogAspect doBefore --------------"); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); //在系統啟動時攔截器經過該位置時會觸發空指針異常,這裡需要進行非空判斷 if (attributes != null) { HttpServletRequest request = attributes.getRequest(); log.info(" 地址: {}", request.getRequestURL().toString()); log.info(" 請求方式: ", request.getMethod()); String ip = request.getRemoteAddr(); if (!IP_LOCALHOST.equals(ip)){ log.info(" 客戶端IP: {}", ip); } log.info(" 請求參數: {}" + Arrays.toString(joinPoint.getArgs())); } } /** * 在請求響應之後,即請求已經經過controller處理返回後 * @param rvt */ @AfterReturning(pointcut = "webLog()",returning = "rvt") public void doAfterReturning(Object rvt){ log.info(" ------------Start WebLogAspect doAfterReturning ------"); log.info(" 響應結果 : {}",rvt.toString()); log.info(" ------------End WebLogAspect doAfterReturning ------"); } /** * 異常切入 * @param error */ @AfterThrowing(pointcut = "webLog()",throwing = "error") public void doAfterThrowing(Throwable error){ log.error(" ------------Start WebLogAspect doAfterThrowing ------"); log.error("請求處理過程中發生異常:{}",error.getMessage()); log.error(" ------------Start WebLogAspect doAfterThrowing ------"); } }
6、進行測試日誌切面
訪問地址:http://localhost:8081/hello?name=cnHuaShao

image.png
至此已經實現訪問時間與日誌的切面,裏面有一些優化還需要更改一下
1、整合切面使用包
在上述代碼中每個方法頭均有一個切入掃描的包路徑,這樣在我們日常使用配置時無法統一化管理,為了解決該問題,特將這種統一的全局值提出來。 更改WebTimeAspect類如下
@Aspect @Component @Order(1) public class WebTimeAspect { private Logger log = LoggerFactory.getLogger(WebTimeAspect.class); /** * 聲明一個線程,用於記錄請求與響應整個周期期間在服務端消耗的時間 */ private ThreadLocal<Long> startTime = new ThreadLocal<>(); /** * com.cnhuashao.rapiddevelopment.core包及所有子包下任何類的任何方法 */ @Pointcut("execution(* com.cnhuashao.rapiddevelopment.core..*.*(..))") public void webTime(){ } /** * 在請求響應之前,即請求到達當前服務端 * 所有demo3下的controller均經過該切面 * 暫存:@Before("within(com.cnhuashao.rapiddevelopment.core..*.*)") * @param joinPoint */ @Before("webTime()") public void doBefore(JoinPoint joinPoint){ log.info("----------- WebTimeAspect doBefore -----------------------------------------"); startTime.set(System.currentTimeMillis()); } /** * 在請求響應之後,即請求已經經過controller處理返回後 * 暫存:@AfterReturning(value = "within(com.cnhuashao.rapiddevelopment.core..*.*)",returning = "rvt") * @param rvt */ @AfterReturning(pointcut = "webTime()",returning = "rvt") public void doAfterReturning(Object rvt){ log.info("-----------Start WebTimeAspect doAfterReturning ------"); log.info("本次處理請求耗費時間 : {}",(System.currentTimeMillis() - startTime.get())); log.info("-----------End WebTimeAspect doAfterReturning -------------------------------"); } }
2、優先級設計
在上述代碼中每個aspect類的頂部都有一個注釋@Order,這個注釋是標記整個切面類的運行優先級的,我們的@Before與@AfterReturning根據order值執行的順序是不一樣的。 @Before order越小越優先 @AfterReturning order越大越優先
3、日常使用時我們應該將全局相關的切麵包與配置包放到base包中,這樣有利於全局化的相關信息管理。
代碼示例
本文的相關例子可以查看倉庫中的RapidDevelopment-demo3
目錄: Gitee 地址
本文聲明:

88×31.png
知識共享許可協議 本作品由 cn華少 採用 知識共享署名-非商業性使用 4.0 國際許可協議 進行許可。