AOP統一處理請求日誌
AOP爲Aspect Oriented Programming的縮寫,意爲:[面向切面編程]
理論就不說了,直接上代碼吧,呵呵~
註解 | 用途 |
---|---|
@Aspect | 註解將一個java類定義爲切面類 |
@Pointcut | 定義一個切入點,可以是一個規則表達式,比如下例中某個package下的所有函數,也可以是一個註解等。根據需要在切入點不同位置的切入內容 |
@Before | 在切入點開始處切入內容 |
@After在 | 切入點結尾處切入內容 |
@AfterReturning | 在切入點return內容之後切入內容(可以用來對處理返回值做一些加工處理) |
@Around在切入 | 點前後切入內容,並自己控制何時執行切入點自身的內容 |
@AfterThrowing | 用來處理當切入內容部分拋出異常之後的處理邏輯 |
一、先建一個切面類文件吧
1.在包目錄右鍵-New-Aspect
建切面類1.png
2.然後會彈出框,輸出類名,下方選擇框一定要選 @Aspect,如下圖:
建切面類2.png
3.工程目錄如下:
工程目錄.png
二、代碼
- 切面類的代碼
@Aspect
@Component
public class TestAspect {
@Before("execution(public * com.alun.Controller.TestController.*(..))")
public void onTestBefore(){
System.out.print("-------------------------------------------調用前\n");
}
@After("execution(public * com.alun.Controller.TestController.*(..))")
public void onTestAfter(){
System.out.print("--------------------------------------------調用後\n");
}
}
- Controller的代碼
@RestController
public class TestController {
@GetMapping("/firstTest")
public String getFirst(){
System.out.print("-----------------我被調用了---------------\n");
return "你好!";
}
}
- 運行結果
運行結果1.png
運行結果2.png
4.打印相關信息
@Aspect
@Component
public class TestAspect {
private final static Logger logger = LoggerFactory.getLogger(TestAspect.class);
@Pointcut("execution(public * com.alun.Controller.TestController.*(..))")
public void testLog() {
}
@Before("testLog()")
public void onTestBefore(JoinPoint joinPoint) {
logger.info("-------------------------------------------調用前\n");
//記錄http請求
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//從request中獲取http請求的url
logger.info("url={}", request.getRequestURI());
///請求的方法類型
logger.info("method={}", request.getMethod());
//IP地址
logger.info("ip={}", request.getRemoteAddr());
//響應該http請求的類方法
logger.info("class_method={}", joinPoint.getSignature().getDeclaringTypeName() +
"." + joinPoint.getSignature().getName());
//請求中的參數
logger.info("args={}", joinPoint.getArgs());
}
@AfterReturning(returning = "object", pointcut = "testLog()")
public void doAfterReturning(Object object) {
logger.info("響應內容={}", object);
}
@After("testLog()")
public void onTestAfter() {
logger.info("--------------------------------------------調用後\n");
}
}
- 這裏使用了org.slf4j.Logger打印日誌,可以輸出更多信息,如時間和相關類
- @Before和@After等括號的內容都一樣,可以提煉出來,使用@Pointcut實現
- @Pointcut的使用:
格式:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
括號中各個pattern分別表示:
- 修飾符匹配(modifier-pattern?)
- 返回值匹配(ret-type-pattern)可以爲*表示任何返回值,全路徑的類名等
- 類路徑匹配(declaring-type-pattern?)
- 方法名匹配(name-pattern)可以指定方法名 或者 代表所有, set 代表以set開頭的所有方法
- 參數匹配((param-pattern))可以指定具體的參數類型,多個參數間用“,”隔開,各個參數也可以用“”來表示匹配任意類型的參數,如(String)表示匹配一個String參數的方法;(,String) 表示匹配有兩個參數的方法,第一個參數可以是任意類型,而第二個參數是String類型;可以用(..)表示零個或多個任意參數
- 異常類型匹配(throws-pattern?)
其中後面跟着“?”的是可選項
這段內容來至於這裏
運行結果:
運行結果
AOP切面的優先級
由於通過AOP實現,程序得到了很好的解耦,但是也會帶來一些問題,比如:我們可能會對
Web層做多個切面,校驗用戶,校驗頭信息等等,這個時候經常會碰到切面的處理順序問題。所以,我們需要定義每個切面的優先級,我們需要@Order(i)註解來標識切面的優先級。i的值越> > 小,優先級越高。假設我們還有一個切面是CheckNameAspect用來校驗name必須爲didi,我們
爲其設置@Order(10),而上文中WebLogAspect設置爲@Order(5),所以WebLogAspect有更高
的優先級,這個時候執行順序是這樣的:在@Before中優先執行@Order(5)的內容,再執行@Order(10)的內容
在@After和@AfterReturning中優先執行@Order(10)的內容,再執行@Order(5)的內容
所以我們可以這樣子總結:在切入點前的操作,按order的值由小到大執行
在切入點後的操作,按order的值由大到小執行