通過AOP攔截Spring Boot紀錄檔並將其存入資料庫

2023-08-29 15:00:20

本文分享自華為雲社群《Spring Boot入門(23):【實戰】通過AOP攔截Spring Boot紀錄檔並將其存入資料庫》,作者:bug菌。

前言

在軟體開發中,常常需要記錄系統執行時的紀錄檔。紀錄檔記錄有助於排查系統問題、優化系統效能、監控操作行為等。本文將介紹如何使用Spring Boot和AOP技術實現攔截系統紀錄檔並儲存到資料庫中的功能。

摘要

本文將通過以下步驟實現攔截系統紀錄檔並儲存到資料庫中的功能:

  1. 設定資料庫連線
  2. 定義紀錄檔實體類
  3. 定義紀錄檔攔截器
  4. 使用AOP攔截紀錄檔並儲存到資料庫中

AOP介紹

AOP,全稱是Aspect Oriented Programming,即面向切面程式設計。AOP的目的是將那些與業務無關,但是業務模組都需要的功能,如紀錄檔統計、安全控制、事務處理等,封裝成可重用的元件,從而將它們從業務邏輯程式碼中劃分出來,編寫成獨立的切面。這樣做,既可以保持業務邏輯的純淨和高內聚性,又可以使得系統的多個模組都可以共用這些公共的功能。

Spring框架提供了對AOP的支援,Spring Boot自然也不例外。使用Spring Boot的AOP功能,我們可以在執行時動態地將程式碼橫向切入到各個關注點(方法或者類)中。這種橫向切面的方式,比傳統的縱向切面(繼承)更加靈活。

AOP的實現

新增依賴

在pom.xml中新增以下依賴:

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-aop</artifactId>

</dependency>

<dependency>

<groupId>org.mybatis.spring.boot</groupId>

<artifactId>mybatis-spring-boot-starter</artifactId>

</dependency>

這樣我們就可以使用Spring Boot的AOP功能和MyBatis框架。

設定資料庫連線

首先需要在Spring Boot專案的application.properties檔案中設定資料庫連線資訊:

spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false

spring.datasource.username=root

spring.datasource.password=123456

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

或者你也可以使用YAML的設定格式:

cke_123.png

定義紀錄檔實體類

定義一個Log實體類用於儲存紀錄檔資訊,並使用@Entity和@Table註解指定對應的資料庫表和欄位:

@Entity

@Table(name = "sys_log")

public class Log {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String username;

private String operation;

private String method;

private String params;

private String ip;

private Date createTime;

// 省略getter和setter方法

}

定義紀錄檔攔截器

定義一個紀錄檔攔截器LogInterceptor,通過實現HandlerInterceptor介面來攔截請求並記錄紀錄檔:

@Component

public class LogInterceptor implements HandlerInterceptor {

@Autowired

private LogRepository logRepository;

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

// 獲取請求的IP地址

String ip = getIpAddress(request);

// 獲取當前使用者

String username = getCurrentUsername();

// 獲取請求的方法名

String method = request.getMethod();

// 獲取請求的URL

String url = request.getRequestURI();

// 獲取請求的引數

String params = getParams(request);

// 建立紀錄檔實體

Log log = new Log();

log.setIp(ip);

log.setMethod(method);

log.setOperation("存取");

log.setParams(params);

log.setUsername(username);

log.setCreateTime(new Date());

// 儲存紀錄檔到資料庫中

logRepository.save(log);



return true;

}

// 省略實現HandlerInterceptor介面的其他方法

/**

* 獲取請求的IP地址

*/

private String getIpAddress(HttpServletRequest request) {

String ip = request.getHeader("X-Forwarded-For");

if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {

ip = request.getHeader("Proxy-Client-IP");

}

if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {

ip = request.getHeader("WL-Proxy-Client-IP");

}

if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {

ip = request.getHeader("HTTP_CLIENT_IP");

}

if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {

ip = request.getHeader("HTTP_X_FORWARDED_FOR");

}

if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {

ip = request.getRemoteAddr();

}

return ip;

}

/**

* 獲取當前使用者

*/

private String getCurrentUsername() {

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

if (authentication != null) {

return authentication.getName();

}

return null;

}

/**

* 獲取請求的引數

*/

private String getParams(HttpServletRequest request) {

Map<String, String[]> parameterMap = request.getParameterMap();

if (parameterMap == null || parameterMap.isEmpty()) {

return null;

}

StringBuilder sb = new StringBuilder();

for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {

sb.append(entry.getKey()).append("=").append(Arrays.toString(entry.getValue())).append("&");

}

return sb.toString();

}

}

使用AOP攔截紀錄檔並儲存到資料庫中

使用AOP技術攔截所有Controller類中的方法,並執行LogInterceptor中的preHandle方法,記錄紀錄檔並儲存到資料庫中。

定義一個LogAspect切面類,通過實現@Aspect註解和@Before註解來實現方法攔截:

@Aspect

@Component

public class LogAspect {

@Autowired

private LogInterceptor logInterceptor;

@Pointcut("execution(public * com.example.demo.controller..*.*(..))")

public void logAspect() {}

@Before("logAspect()")

public void doBefore(JoinPoint joinPoint) {

ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

if (attributes == null) {

return;

}

HttpServletRequest request = attributes.getRequest();

HttpServletResponse response = attributes.getResponse();

HandlerMethod handlerMethod = (HandlerMethod) joinPoint.getSignature();

try {

logInterceptor.preHandle(request, response, handlerMethod);

} catch (Exception e) {

e.printStackTrace();

}

}

}

程式碼方法介紹

  • LogInterceptor.preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)方法:攔截請求並記錄紀錄檔的方法。
  • LogInterceptor.getIpAddress(HttpServletRequest request)方法:獲取請求的IP地址。
  • LogInterceptor.getCurrentUsername()方法:獲取當前使用者。
  • LogInterceptor.getParams(HttpServletRequest request)方法:獲取請求的引數。
  • LogAspect.logAspect()方法:定義AOP切入點,攔截Controller類中的所有方法。
  • LogAspect.doBefore(JoinPoint joinPoint)方法:執行方法攔截操作,並呼叫LogInterceptor.preHandle方法來記錄紀錄檔。

測試用例

可以使用Postman等工具發起請求來測試攔截器是否生效,並檢視資料庫中是否儲存了對應的紀錄檔資訊。這裡就不直接演示了,畢竟使用起來非常的簡單易上手。

全文小結

本文介紹瞭如何使用Spring Boot和AOP技術實現攔截系統紀錄檔並儲存到資料庫中的功能,包括設定資料庫連線、定義紀錄檔實體類、定義紀錄檔攔截器、使用AOP攔截紀錄檔並儲存到資料庫中等步驟。通過本文的介紹,可以更好地理解Spring Boot和AOP的應用,為開發高效、穩定的系統提供參考。

注:

環境說明:Windows10 + Idea2021.3.2 + Jdk1.8 + SpringBoot 2.3.1.RELEASE

 

點選關注,第一時間瞭解華為雲新鮮技術~