六、集成全局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 國際許可協議 進行許可。