AOP 是 Spring 體系中非常重要的兩個概念之一(另外一個是 IoC),今天這篇文章就來帶大家通過實戰的方式,在編程貓 SpringBoot 專案中使用 AOP 技術為 controller 層添加一個切面來實作介面訪問的統一日志記錄,
一、關于 AOP
AOP,也就是 Aspect-oriented Programming,譯為面向切面編程,是計算機科學中的一個設計思想,旨在通過切面技術為業務主體增加額外的通知(Advice),從而對宣告為“切點”(Pointcut)的代碼塊進行統一管理和裝飾,
這種思想非常適用于,將那些與核心業務不那么密切關聯的功能添加到程式中,就好比我們今天的主題——日志功能,就是一個典型的案例,

AOP 是對面向物件編程(Object-oriented Programming,俗稱 OOP)的一種補充,OOP 的核心單元是類(class),而 AOP 的核心單元是切面(Aspect),利用 AOP 可以對業務邏輯的各個部分進行隔離,從而降低耦合度,提高程式的可重用性,同時也提高了開發效率,
我們可以簡單的把 AOP 理解為貫穿于方法之中,在方法執行前、執行時、執行后、回傳值后、例外后要執行的操作,
二、AOP 的相關術語
來看下面這幅圖,這是一個 AOP 的模型圖,就是在某些方法執行前后執行一些通用的操作,并且這些操作不會影響程式本身的運行,

我們了解下 AOP 涉及到的 5 個關鍵術語:
1)橫切關注點,從每個方法中抽取出來的同一類非核心業務
2)切面(Aspect),對橫切關注點進行封裝的類,每個關注點體現為一個通知方法;通常使用 @Aspect 注解來定義切面,
3)通知(Advice),切面必須要完成的各個具體作業,比如我們的日志切面需要記錄介面呼叫前后的時長,就需要在呼叫介面前后記錄時間,再取差值,通知的方式有五種:
- @Before:通知方法會在目標方法呼叫之前執行
- @After:通知方法會在目標方法呼叫后執行
- @AfterReturning:通知方法會在目標方法回傳后執行
- @AfterThrowing:通知方法會在目標方法拋出例外后執行
- @Around:把整個目標方法包裹起來,在被呼叫前和呼叫之后分別執行通知方法
4)連接點(JoinPoint),通知應用的時機,比如介面方法被呼叫時就是日志切面的連接點,
5)切點(Pointcut),通知功能被應用的范圍,比如本篇日志切面的應用范圍是所有 controller 的介面,通常使用 @Pointcut 注解來定義切點運算式,
切入點運算式的語法格式規范如下所示:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?
name-pattern(param-pattern)
throws-pattern?)
modifiers-pattern?為訪問權限修飾符ret-type-pattern為回傳型別,通常用*來表示任意回傳型別declaring-type-pattern?為包名name-pattern為方法名,可以使用*來表示所有,或者set*來表示所有以 set 開頭的類名param-pattern)為引數型別,多個引數可以用,隔開,各個參與也可以使用*來表示所有型別的引數,還可以使用(..)表示零個或者任意引數throws-pattern?為例外型別?表示前面的為可選項
舉個例子:
@Pointcut("execution(public * com.codingmore.controller.*.*(..))")
表示 com.codingmore.controller 包下的所有 public 方法都要應用切面的通知,
三、實操 AOP 記錄介面訪問日志
第一步,在 Spring Boot 專案的 pom.xml 檔案中添加 spring-boot-starter-aop 依賴,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
第二步,添加日志資訊封裝類 WebLog,用于記錄什么樣的操作、操作的人是誰、開始時間、花費的時間、操作的路徑、操作的方法名、操作主機的 IP、請求引數、回傳結果等,
/**
* Controller層的日志封裝類
* Created by macro on 2018/4/26.
*/
public class WebLog {
private String description;
private String username;
private Long startTime;
private Integer spendTime;
private String basePath;
private String uri;
private String url;
private String method;
private String ip;
private Object parameter;
private Object result;
//省略了getter,setter方法
}
第三步,添加統一日志處理切面 WebLogAspect,
/**
* 統一日志處理切面
* Created by 石磊
*/
@Aspect
@Component
@Order(1)
public class WebLogAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(WebLogAspect.class);
@Pointcut("execution(public * com.codingmore.controller.*.*(..))")
public void webLog() {
}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
}
@AfterReturning(value = "https://www.cnblogs.com/qing-gee/archive/2022/02/22/webLog()", returning = "ret")
public void doAfterReturning(Object ret) throws Throwable {
}
@Around("webLog()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
//獲取當前請求物件
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//記錄請求資訊(通過Logstash傳入Elasticsearch)
WebLog webLog = new WebLog();
Object result = joinPoint.proceed();
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method.isAnnotationPresent(ApiOperation.class)) {
ApiOperation log = method.getAnnotation(ApiOperation.class);
webLog.setDescription(log.value());
}
long endTime = System.currentTimeMillis();
String urlStr = request.getRequestURL().toString();
webLog.setBasePath(StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath()));
webLog.setIp(request.getRemoteUser());
Map<String,Object> logMap = new HashMap<>();
logMap.put("spendTime",webLog.getSpendTime());
logMap.put("description",webLog.getDescription());
LOGGER.info("{}", JSONUtil.parse(webLog));
return result;
}
}
第四步,運行專案,并對 controller 下的某個控制器進行測驗,
Swagger knife4j 訪問地址:http://localhost:9022/doc.html
執行登錄用戶查詢操作:

可以在控制臺可以看到以下日志資訊:

原始碼地址:
https://github.com/itwanger/coding-more
參考鏈接:
作者 cxuan:https://www.cnblogs.com/cxuanBlog/p/13060510.html
灰小猿:https://bbs.huaweicloud.com/blogs/289045
山高我為峰:https://www.cnblogs.com/liaojie970/p/7883687.html
macrozheng:https://github.com/macrozheng/mall
本篇已收錄至 GitHub 上星標 1.6k+ star 的開源專欄《Java 程式員進階之路》,據說每一個優秀的 Java 程式員都喜歡她,風趣幽默、通俗易懂,內容包括 Java 基礎、Java 并發編程、Java 虛擬機、Java 企業級開發、Java 面試等核心知識點,學 Java,就認準 Java 程式員進階之路??,
https://github.com/itwanger/toBeBetterJavaer
star 了這個倉庫就等于你擁有了成為了一名優秀 Java 工程師的潛力,也可以戳下面的鏈接跳轉到《Java 程式員進階之路》的官網網址,開始愉快的學習之旅吧,
https://tobebetterjavaer.com/

沒有什么使我停留——除了目的,縱然岸旁有玫瑰、有綠蔭、有寧靜的港灣,我是不系之舟,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/431000.html
標籤:其他
