2021年2月2日 星期二

[SpringBoot 1.5] AOP概念及日誌範例

關於橫切面AOP的思維

個人的領會是, 在程式進行的流程為一個直向的流層, 但是, 如果要在其中幾個物件被使用的時候,都插入一段程式碼, 這種的思想就是橫切面設計, 

具體的範例, 是一種鑲嵌, 縫合程式碼的思維, 比方說在每一次從某些controller的時候都警示該功能已經被呼叫, 就是都額外run一段程式碼, 如果說要在每個controller上面加上這段, 就是一個麻煩的事, 如果我們在後面才設計出要被加入的這段程式碼, 那還不如能用縫合的, 將其呼叫的時機設定好, 然後不用每個controller都重新鑲入該程式碼, 那這下面會有些關鍵字的思維可以釐清


1 切面Aspect

切面是一個模塊關注點的模塊化, 他由切入點PointCut和通知Advice組成

@Aspect
@Component
public class LogAspect {
}


2 目標Target

將要被縫合的類別, 比方說, 我們就設計了一個controller, 等下測試縫合程式碼的執行順序

@Controller
public class IndexController {

@GetMapping("/{id}/{name}")
public String index(@PathVariable Integer id, @PathVariable String name){
System.out.println("~~~~~~~~~~~~index~~~~~~~~~~");
return "index";
}
}


3 連接點JoinPoint

被視為切入連接點, 是下列範例中的log()方法


@Before("log()")
public void doBefore(JoinPoint joinPoint){
 logger.info("~~~~~~~Before~~~~~~~");
}


4 切入點PointCut

而切入點則是下面括號中指定execution中的東西,* 是指說返回任何東西

 com.lrm.web.*.*(..)是AOP要切入的對象, web後面*表示任何類,第二個*表示任何方法

而(..)表示任何參數, 當然也可以使用web.IndexController 這樣就只有這個Controller會被

橫切插入程式

@Pointcut("execution(* com.lrm.web.*.*(..))")
public void log(){
}


5 通知Advice

有不同被通知的種類, 比方說Before, After, 

@Before("log()")
public void doBefore(JoinPoint joinPoint){
 logger.info("~~~~~~~Before~~~~~~~");
}

@After("log()")
public void doAfter(){
logger.info("~~~~~~~After~~~~~~~");

}


6 織入Weaving

將切面Aspect與對象Target連接起來, 織入可以在類別加載的的時候完成, 也可以在編譯, 或是運行的時候完成...如果是編譯時完成就是靜態代理, 如果在運行時完成就是動態代理

--------------------------------------------------------------------------------------

以下是一個完整的程式碼

會設計一個內部類別, 然後將其Url, ip, 目標的物件名稱及參數都印出來

package com.lrm.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

@Aspect
@Component
public class LogAspect {

private final Logger logger= LoggerFactory.getLogger(this.getClass());
@Pointcut("execution(* com.lrm.web.*.*(..))")
public void log(){

}

@Before("log()")
public void doBefore(JoinPoint joinPoint){
logger.info("~~~~~~~before~~~~~~~");
ServletRequestAttributes attributes=(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request=attributes.getRequest();
String url =request.getRequestURL().toString();
String ip=request.getRemoteAddr();
String classMethod=joinPoint.getSignature().getDeclaringTypeName() + "." +joinPoint.getSignature().getName();
Object[] args= joinPoint.getArgs();
RequestLog requestLog= new RequestLog(url,ip,classMethod,args);
logger.info("Request : {}",requestLog);
}

@After("log()")
public void doAfter(){
logger.info("~~~~~~~After~~~~~~~");

}

@AfterReturning(returning = "result" , pointcut = "log()")
public void doAfterReturn(Object result){
logger.info("Result: {}",result);
}

private class RequestLog{
private String url;
private String ip;
private String classMethod;
private Object[] args;

public RequestLog(String url, String ip, String classMethod, Object[] args) {
this.url = url;
this.ip = ip;
this.classMethod = classMethod;
this.args = args;
}

@Override
public String toString() {
return "RequestLog{" +
"url='" + url + '\'' +
", ip='" + ip + '\'' +
", classMethod='" + classMethod + '\'' +
", args=" + Arrays.toString(args) +
'}';
}
}
}

再搭配上前面的IndexController, 開啟然後輸入 http://localhost:8081/1/hat

後面兩個參數是先隨意給的, 以下是結果的截圖




參考網址:

https://zhuanlan.zhihu.com/p/60842627

https://matthung0807.blogspot.com/2018/02/spring-aop.html

沒有留言:

張貼留言

[leetcode] [KMP] KMP

ABCDABD... ABCDABF... 簡單的說, 傳統解兩字串匹配部分 可能會來個雙迴圈, 哀個比對, 當不匹配的時候, 會將下方列再後移1位 然後不匹配再後移 然而 如果像上放已經有4個屬於匹配的字串, 她就應該直接往後移四位來匹配, 而不是只移動1位 隱藏的思維是, 當...