SpringBoot部落格開發之AOP日誌處理

xbhog發表於2021-08-31

日誌處理:

需求分析

日誌處理需要記錄的是:

  1. 請求的URL
  2. 訪問者IP
  3. 呼叫的方法
  4. 傳入的引數
  5. 返回的內容

上面的內容要求在控制檯和日誌中輸出。

在學習這部分知識的時候,真的感覺收穫很多,在之前Spring學習的aop只是初步瞭解,現在有了一些深入的理解。好記性不如爛筆頭!

在日誌處理這部分主要是aop的使用,通過切面的方式來整合到專案了,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

人話:Aop 在不改變原有程式碼的情況下 , 去增加新的功能

需要了解的:

  • 橫切關注點:跨越應用程式多個模組的方法或功能。即是,與我們業務邏輯無關的,但是我們需要關注的部分,就是橫切關注點。如日誌 , 安全 , 快取 , 事務等等 ....
  • 切面(ASPECT):橫切關注點 被模組化 的特殊物件。即,它是一個類。
  • 通知(Advice):切面必須要完成的工作。即,它是類中的一個方法。
  • 切入點(PointCut):切面通知 執行的 “地點”的定義。
  • 連線點(JointPoint):與切入點匹配的執行點。

通知(Advice)裡面還有幾種方法來幫助實現,這裡我列舉了該部分實現的方法:

  1. doBefore方法(方法前執行),需要註解@Before實現
  2. After方法(方法後執行),需要註解@After實現
  3. doAfterReturning方法,需要註解@AfterReturning實現

具體實現看後面部分。

重要部分:匯入依賴

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <scope>test</scope>
</dependency>

提醒:匯入包以後,重新整理下Maven,如果執行的時候找不到包,重啟試試,網上有很多的解決方法(也試過),最後個人只是重啟一下子再重新整理下maven就可以使用了。

這裡就沒有給出解決方法,只是一個提醒。

實現過程:

建立一個類(LogAspect),將該類定義成一個切面(@Aspect)並且加入容器中(@Component)。

首先建立一個切入點,後面的Advice是建立在切入點上:

@Pointcut("execution(* com.blog.Controller..*.*(..))")
public void log(){}

整個表示式可以分為五個部分

1、execution():表示式主體。
2、第一個*號:表示返回型別,*號表示所有的型別。
3、包名:表示需要攔截的包名,後面的兩個句點分別表示當前包和當前包的所有子包,com.blog.Controller包、子孫包下所有類的方法。
4、第二個*號:表示類名,*號表示所有的類。
5、*(..) :第三個星號表示方法名,*號表示所有的方法,後面括弧裡面表示方法的引數,兩個句點表示任何引數

定義完切入點後,處理前置通知和後置通知:

@Before("log()")
public void doBefore(JoinPoint joinPoint){
    System.out.println("在進入controller之前處理流-------------");
}
@After("log()")
public void doAfter(){
    System.out.println("在進入controller之後處理流-------------");
}
//在切入點return內容之後切入內容(可以用來對處理返回值做一些加工處理)
@AfterReturning(returning = "result",pointcut="log()")
public void doAfterReturning(Object result){
    logger.info("Return ------ {}",result );
}

通過上述的簡單介紹,可以知道我們如果需要知道需求裡面的要求,我們的重點應該放到前置通知裡面,在流處理之前獲取前端操作的資訊。

核心程式碼如下:

//通過上下文來獲取請求裡面的資訊
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

HttpServletRequest request = attributes.getRequest();
//獲取封裝了署名資訊的物件,在該物件中可以獲取到目標方法名,所屬類的Class等資訊(反射)
String classMethod = joinPoint.getSignature().getDeclaringTypeName()+","+joinPoint.getSignature().getName();
// 1. 獲取URL
String url = request.getRequestURL().toString();
//2. 獲取ip地址
String addr = request.getRemoteAddr();

/*建立一個類RequestData,來儲存相關資訊*/
RequestData requestData = new RequestData(
    url, addr, classMethod, joinPoint.getArgs()
);
//在控制檯列印出來
logger.info("RequestData------{}",requestData);

建立的類是內部類(RequestData),只是封裝一下需要列印的資訊。

實驗效果:

在進入controller之前處理流-------------
2021-08-15 15:19:43.923  INFO 9644 --- [nio-8080-exec-1] com.blog.AspectAop.LogAspect             : RequestData------RequestData{url='http://localhost:8080/', ipAddr='0:0:0:0:0:0:0:1', classMethod='com.blog.Controller.IndexController,index', args=[]}
2021-08-15 15:19:43.932  INFO 9644 --- [nio-8080-exec-1] com.blog.AspectAop.LogAspect             : Return ------ index
在進入controller之後處理流-------------

專案的github地址

參考文獻:

狂神說Spring

表示式引數

結束:

如果你看到這裡或者正好對你有所幫助,希望能點個關注或者推薦,感謝;

有錯誤的地方,歡迎在評論指出,作者看到會進行修改。

相關文章