SpringBoot中日志切面實(shí)現(xiàn)小結(jié)
在應(yīng)用開發(fā)中,日志記錄對(duì)于監(jiān)控、調(diào)試和追蹤用戶行為至關(guān)重要。Spring Boot 雖然內(nèi)置了強(qiáng)大的日志框架,但在某些情況下,我們可能需要更細(xì)粒度的日志管理。Spring AOP 提供了一種靈活的方式來實(shí)現(xiàn)方法級(jí)別的日志記錄,而無需侵入業(yè)務(wù)代碼。本文將介紹如何通過 Spring AOP 切面來實(shí)現(xiàn)這一功能。
什么是Spring AOP?
Spring AOP 是一個(gè)面向切面的編程(AOP)框架,它允許開發(fā)者將橫切關(guān)注點(diǎn)(如日志記錄、事務(wù)管理等)與業(yè)務(wù)邏輯分離。通過使用 Spring AOP,我們可以在不修改業(yè)務(wù)代碼的情況下,為應(yīng)用程序添加日志記錄功能。
Spring AOP 詳細(xì)可以看這篇文章 Spring AOP入門
日志切面的設(shè)計(jì)
在我們的示例中,我們定義了一個(gè) SysLog 注解,用于標(biāo)記需要記錄日志的方法。接著,我們創(chuàng)建了一個(gè)日志切面 LogAspect,它會(huì)攔截所有帶有 SysLog 注解的方法,并記錄日志信息。
SysLog 注解
首先,我們定義了一個(gè) SysLog 注解,它可以用來標(biāo)記需要記錄日志的方法:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String value() default "";
}這個(gè)注解非常簡(jiǎn)單,它只有一個(gè) value 屬性,用于存儲(chǔ)日志的描述信息。
日志切面 LogAspect
接下來,我們創(chuàng)建了 LogAspect 切面類,它會(huì)攔截所有帶有 SysLog 注解的方法:
@Aspect
@Component
public class LogAspect {
// 省略其他成員變量和方法
@Around("@annotation(sysLog)")
public Object logAround(ProceedingJoinPoint joinPoint, SysLog sysLog) throws Throwable {
// 日志記錄邏輯
}
}在這個(gè)切面中,我們使用了 @Around 注解來定義一個(gè)環(huán)繞通知,它會(huì)在目標(biāo)方法執(zhí)行前后記錄日志信息。
日志記錄的實(shí)現(xiàn)
在 LogAspect 切面類中,我們實(shí)現(xiàn)了 logAround 方法,它會(huì)在目標(biāo)方法執(zhí)行前后記錄日志信息:
@Around("@annotation(sysLog)")
public Object logAround(ProceedingJoinPoint joinPoint, SysLog sysLog) throws Throwable {
Log log = new Log();
log.setTimestamp(new Date());
log.setDescription(sysLog.value());
log.setMethodName(joinPoint.getSignature().getName());
log.setParameters(Arrays.toString(joinPoint.getArgs()));
try {
Object result = joinPoint.proceed();
log.setLevel("INFO");
log.setMessage("方法 " + joinPoint.getSignature().getName() + " 執(zhí)行成功。");
return result;
} catch (Exception e) {
log.setLevel("ERROR");
log.setMessage("方法 " + joinPoint.getSignature().getName() + " 執(zhí)行過程中發(fā)生異常。");
log.setException(e.toString());
throw e;
} finally {
logService.save(log);
}
}在這個(gè)環(huán)繞通知中,我們首先創(chuàng)建了一個(gè) Log 對(duì)象,并設(shè)置了日志的基本信息,如時(shí)間戳、描述、方法名和參數(shù)。然后,我們執(zhí)行目標(biāo)方法,并根據(jù)執(zhí)行結(jié)果記錄日志信息。如果目標(biāo)方法執(zhí)行過程中發(fā)生異常,我們會(huì)記錄異常信息。
測(cè)試


完整代碼
Maven依賴
在 pom.xml 文件中添加依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>SQL腳本
創(chuàng)建日志表的 SQL 腳本:
DROP TABLE IF EXISTS `logs`; CREATE TABLE `logs` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主鍵ID', `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '描述', `level` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '日志級(jí)別', `timestamp` datetime NOT NULL COMMENT '時(shí)間戳', `message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '日志消息', `class_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '類名', `method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '方法名', `parameters` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '參數(shù)', `user_identifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用戶標(biāo)識(shí)', `exception` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '異常信息', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '日志表' ROW_FORMAT = DYNAMIC;
日志實(shí)體類 Log
我們定義了一個(gè) Log 實(shí)體類,用于存儲(chǔ)日志信息:
/**
* 日志實(shí)體類
*
* @author 王聞薪
*/
@Data
@TableName("logs")
public class Log implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主鍵ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 描述
*/
@TableField("description")
private String description;
/**
* 日志級(jí)別
*/
@TableField("level")
private String level;
/**
* 時(shí)間戳
*/
@TableField("timestamp")
private Date timestamp;
/**
* 日志消息
*/
@TableField("message")
private String message;
/**
* 類名
*/
@TableField("class_name")
private String className;
/**
* 方法名
*/
@TableField("method_name")
private String methodName;
/**
* 參數(shù)
*/
@TableField("parameters")
private String parameters;
/**
* 用戶標(biāo)識(shí)
*/
@TableField("user_identifier")
private String userIdentifier;
/**
* 異常信息
*/
@TableField("exception")
private String exception;
}這個(gè)實(shí)體類映射到數(shù)據(jù)庫(kù)中的 logs 表,用于存儲(chǔ)日志的詳細(xì)信息。
切面代碼
日志切面類 LogAspect:
package org.example.demo.aspectj;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.example.demo.annotation.SysLog;
import org.example.demo.entity.Log;
import org.example.demo.service.ILogService;
import org.example.demo.utils.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
/**
* 日志切面類
* 用于攔截標(biāo)注有@SysLog注解的方法,并記錄日志信息。
*
* @author 王聞薪
*/
@Slf4j
@Aspect // 標(biāo)記為切面類
@Component // 將此切面類交給Spring管理
public class LogAspect {
@Autowired
private ILogService logService;
@Autowired
private TokenService tokenService;
/**
* 環(huán)繞通知
* 攔截帶有@SysLog注解的方法,并在方法執(zhí)行前后進(jìn)行日志記錄。
*
* @param joinPoint 連接點(diǎn)對(duì)象,包含方法的調(diào)用信息。
* @param sysLog 注解對(duì)象,包含日志描述信息。
* @return 方法執(zhí)行結(jié)果。
* @throws Throwable 可能拋出的異常。
*/
@Around("@annotation(sysLog)")
public Object logAround(ProceedingJoinPoint joinPoint, SysLog sysLog) throws Throwable {
// 創(chuàng)建日志對(duì)象
Log log = new Log();
log.setLevel("INFO");
log.setTimestamp(new Date());
log.setUserIdentifier(getUserId().toString());
log.setDescription(sysLog.value());
// 記錄方法調(diào)用前的日志
logBefore(joinPoint, log);
try {
// 繼續(xù)執(zhí)行目標(biāo)方法
Object result = joinPoint.proceed();
// 記錄方法調(diào)用后的日志
logAfter(joinPoint, result, log);
log.setMessage("方法 " + joinPoint.getSignature().getName() + " 執(zhí)行成功。");
return result;
} catch (Exception e) {
// 記錄異常日志
logException(joinPoint, e, log);
log.setLevel("ERROR");
log.setMessage("方法 " + joinPoint.getSignature().getName() + " 執(zhí)行過程中發(fā)生異常。");
throw e; // 重新拋出異常
} finally {
// 保存日志到數(shù)據(jù)庫(kù)
logService.save(log);
}
}
/**
* 記錄方法調(diào)用前的日志信息。
*
* @param joinPoint 連接點(diǎn)對(duì)象,包含方法的調(diào)用信息。
* @param log 日志對(duì)象,用于存儲(chǔ)日志信息。
*/
private void logBefore(ProceedingJoinPoint joinPoint, Log log) {
log.setClassName(joinPoint.getSignature().getDeclaringTypeName());
log.setMethodName(joinPoint.getSignature().getName());
log.setParameters(Arrays.toString(joinPoint.getArgs()));
log.setTimestamp(new Date()); // 設(shè)置日志時(shí)間
}
/**
* 記錄方法調(diào)用后的日志信息。
*
* @param joinPoint 連接點(diǎn)對(duì)象,包含方法的調(diào)用信息。
* @param result 方法執(zhí)行結(jié)果。
* @param log 日志對(duì)象,用于存儲(chǔ)日志信息。
*/
private void logAfter(ProceedingJoinPoint joinPoint, Object result, Log log) {
log.setMessage(log.getMessage() + result);
}
/**
* 記錄方法執(zhí)行過程中的異常信息。
*
* @param joinPoint 連接點(diǎn)對(duì)象,包含方法的調(diào)用信息。
* @param e 捕獲的異常對(duì)象。
* @param log 日志對(duì)象,用于存儲(chǔ)日志信息。
*/
private void logException(ProceedingJoinPoint joinPoint, Exception e, Log log) {
log.setException(e.toString());
log.setMessage(log.getMessage() + " 異常信息:" + e.getMessage());
}
/**
* 獲取當(dāng)前用戶ID。
*
* @return 用戶ID。
*/
private Long getUserId() {
return tokenService.getUserId();
}
}結(jié)論
通過使用 Spring AOP 和注解,我們可以靈活地為 Spring Boot 應(yīng)用程序添加日志記錄功能,而無需修改業(yè)務(wù)邏輯代碼。這不僅提高了代碼的可維護(hù)性,還使得日志記錄變得更加方便和強(qiáng)大。
到此這篇關(guān)于SpringBoot中日志切面實(shí)現(xiàn)小結(jié)的文章就介紹到這了,更多相關(guān)SpringBoot 日志切面內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringSecurity JWT基于令牌的無狀態(tài)認(rèn)證實(shí)現(xiàn)
Spring Security中實(shí)現(xiàn)基于JWT的無狀態(tài)認(rèn)證是一種常見的做法,本文就來介紹一下SpringSecurity JWT基于令牌的無狀態(tài)認(rèn)證實(shí)現(xiàn),感興趣的可以了解一下2025-04-04
Java的Spring框架中bean的繼承與內(nèi)部bean的注入
這篇文章主要介紹了Java的Spring框架中bean的繼承與內(nèi)部bean的注入,Spring框架是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-12-12
IDEA 創(chuàng)建一個(gè)Mybatis Maven項(xiàng)目的方法步驟(圖文)
這篇文章主要介紹了IDEA 創(chuàng)建一個(gè)Mybatis Maven項(xiàng)目的方法步驟(圖文),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
MyBatis自定義映射resultMap的實(shí)現(xiàn)
本文主要介紹了MyBatis自定義映射resultMap的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
SpringMVC中Controller層獲取前端請(qǐng)求參數(shù)的方式匯總
這篇文章主要介紹了SpringMVC中Controller層獲取前端請(qǐng)求參數(shù)的幾種方式,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08
使用java自帶des加密算法實(shí)現(xiàn)文件加密和字符串加密
這篇文章主要介紹了使用java自帶des加密算法實(shí)現(xiàn)文件加密和字符串加密的示例,需要的朋友可以參考下2014-03-03
java中hasNextInt判斷后無限循環(huán)輸出else項(xiàng)的解決方法
這篇文章主要介紹了java中hasNextInt判斷后無限循環(huán)輸出else項(xiàng)的解決方法的相關(guān)資料,需要的朋友可以參考下2016-10-10
Spring框架+jdbcTemplate實(shí)現(xiàn)增刪改查功能
這篇文章主要介紹了Spring框架+jdbcTemplate實(shí)現(xiàn)增刪改查功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09

