接口日志拦截基础版
# 日志拦截基础版
在 Spring 框架中,AOP、拦截器和过滤器都是常见的技术,可以在不同层次进行请求的拦截和处理。它们的执行顺序和应用场景各不相同,因此理解它们的工作原理及最佳应用场景对开发高效、灵活的企业级应用非常重要。
# AOP 和拦截器的执行顺序
在处理一个 HTTP 请求时,Spring 会按照以下顺序执行:
- 过滤器(Filter):最早执行,用于全局请求处理,可以在请求到达 Spring 容器前进行拦截。这是处理请求的第一道关卡,适合做日志记录、跨域处理、认证等操作。
- 拦截器(HandlerInterceptor):在过滤器之后执行,它是在进入控制器方法之前和之后进行操作,适合处理权限校验、参数校验等业务逻辑。
- AOP 切面(Aspect):在拦截器之后执行,作用于方法层级,具体拦截的是目标方法的执行过程,适合进行业务逻辑层级的日志记录、性能监控和异常处理等操作。
为什么 AOP 无法拦截拦截器之前的请求内容
AOP 是基于 Spring 的代理机制(JDK 动态代理或 CGLIB)实现的,它只能拦截方法的执行。AOP 的本质是方法级别的切面编程,因此无法在拦截器之前获取请求内容。拦截器是在 Controller 方法执行之前就已经处理的,而 AOP 只能在方法实际调用时生效。
典型的使用场景
- 全局日志记录(包括请求内容):使用过滤器,可以在请求进入 Spring 容器前记录详细日志信息。
- 业务逻辑处理、权限校验:使用拦截器,可以在控制器方法调用前对请求进行处理。
- 方法级别的日志或异常处理:使用 AOP,适合在业务逻辑层面或数据访问层面进行日志和异常处理。
# 使用 Spring AOP 实现日志拦截
在企业开发中,日志拦截是常见的需求,使用 Spring AOP 可以方便地实现方法级别的日志记录。Spring AOP 提供了强大的切面编程能力,可以在方法执行的前后进行日志记录,捕捉方法的执行过程和异常情况。下面将详细介绍如何通过 Spring AOP 实现日志拦截。
# 1. 引入依赖
首先,我们需要在 pom.xml
中引入 AOP 相关的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2
3
4
# 2. 创建日志切面类
在日志切面类中,我们可以定义切入点、前置通知、后置通知和异常通知。以下是详细代码示例:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* 日志切面类,用于拦截 Controller 层的请求并记录日志
*/
@Aspect
@Component
public class LogAspect {
// 使用 SLF4J 记录日志
private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
/**
* 定义切入点,拦截所有 com.yourpackage.controller 包及子包下的方法
* execution 语法详解:
* - 第一个 * 表示任意返回类型
* - 包名路径表示需要拦截的类(可以使用 .. 表示包及其子包)
* - 第二个 * 表示方法名称(* 表示任意方法)
* - (..) 表示任意参数
*/
@Pointcut("execution(* com.yourpackage.controller..*(..))")
public void logPointcut() {}
/**
* 在目标方法执行前执行,记录方法名称和请求参数
* @param joinPoint 包含了方法的签名、参数等信息
*/
@Before("logPointcut()")
public void logBefore(JoinPoint joinPoint) {
// 获取请求的方法名称
String methodName = joinPoint.getSignature().getName();
// 获取请求的参数
Object[] args = joinPoint.getArgs();
// 记录日志
logger.info("请求方法: {}", methodName);
logger.info("请求参数: {}", args);
}
/**
* 在目标方法成功执行后执行,记录返回值
* @param joinPoint 依旧可以获取方法签名和参数
* @param result 方法的返回值
*/
@AfterReturning(value = "logPointcut()", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
// 记录返回值
logger.info("响应结果: {}", result);
}
/**
* 在目标方法抛出异常时执行,记录异常信息
* @param joinPoint 同样可以获取方法签名、参数等信息
* @param exception 捕获到的异常
*/
@AfterThrowing(value = "logPointcut()", throwing = "exception")
public void logAfterThrowing(JoinPoint joinPoint, Exception exception) {
// 记录异常信息
logger.error("请求方法: {} 发生异常: {}", joinPoint.getSignature().getName(), exception.getMessage());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# 3. 关键 API 和参数说明
@Pointcut
: 用于定义切入点表达式,控制需要拦截的方法范围。execution(* com.yourpackage.controller..*(..))
: 表示拦截com.yourpackage.controller
包及其子包下的所有方法。
@Before
: 在目标方法执行前执行,适合记录请求信息。JoinPoint joinPoint
: 传递了当前被拦截方法的签名和参数。
@AfterReturning
: 在目标方法正常执行后执行,适合记录返回值。@AfterReturning(value = "logPointcut()", returning = "result")
:returning
表示接收方法的返回值。
@AfterThrowing
: 在目标方法抛出异常时执行,适合记录异常信息。@AfterThrowing(value = "logPointcut()", throwing = "exception")
:throwing
表示捕获异常对象。
# 使用 Spring MVC 的 HandlerInterceptor 实现日志拦截
使用 Spring MVC 的 HandlerInterceptor
可以直接对 HTTP 请求进行拦截,记录请求和响应的详细信息,拦截器更适合对整个控制器层面的请求进行统一日志管理。
# 1. 引入依赖
Spring Boot Web 模块已经默认引入了相关依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2
3
4
# 2. 创建自定义拦截器
以下是通过自定义拦截器实现日志记录的完整代码:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 自定义日志拦截器,用于拦截 HTTP 请求并记录日志
*/
@Component
public class LogInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);
/**
* 在请求处理前执行,记录请求的基本信息
* @param request HTTP 请求对象
* @param response HTTP 响应对象
* @param handler 处理器对象(通常是控制器方法)
* @return 返回 true 表示继续执行后续操作
* @throws Exception 异常处理
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 记录请求的 URL
logger.info("请求 URL: {}", request.getRequestURL().toString());
// 记录请求的 HTTP 方法(GET、POST 等)
logger.info("请求方法: {}", request.getMethod());
// 记录请求的 IP 地址
logger.info("请求 IP: {}", request.getRemoteAddr());
return true; // 返回 true 继续执行请求
}
/**
* 在请求处理完成后执行(包括异常情况),记录响应状态和异常信息
* @param request HTTP 请求对象
* @param response HTTP 响应对象
* @param handler 处理器对象
* @param ex 异常对象(如果存在)
* @throws Exception 异常处理
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 记录响应的状态码
logger.info("响应状态码: {}", response.getStatus());
// 如果有异常,记录异常信息
if (ex != null) {
logger.error("请求异常: {}", ex.getMessage());
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 3. 注册拦截器
拦截器需要在 Spring MVC 的配置中进行注册:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 拦截器配置类,用于注册自定义拦截器
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LogInterceptor logInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册拦截器,拦截所有请求路径
registry.addInterceptor(logInterceptor).addPathPatterns("/**");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 4. 关键 API 和参数说明
preHandle
: 在请求处理前执行,用于记录请求的基本信息。HttpServletRequest request
: 请求对象,包含了请求的 URL、HTTP 方法、IP 地址等信息。
afterCompletion
: 在请求完成后执行(包括异常情况),用于记录响应状态和异常信息。HttpServletResponse response
: 响应对象,包含了响应状态码。
# 使用 Logback + Slf4j 配合过滤器实现日志拦截
在一些情况下,可能需要更细粒度的日志控制,可以通过自定义过滤器配合 Logback 进行日志记录。
# 1. 引入依赖
如果项目中尚未使用 Logback 作为日志框架,需要引入以下依赖:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
2
3
4
5
# 2. 配置 Logback
Logback 的配置文件通常位于 src/main/resources/logback.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 日志级别配置 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3. 自定义日志过滤器
通过过滤器实现日志拦截,拦截所有的 HTTP 请求:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* 自定义日志过滤器,拦截所有 HTTP 请求并记录日志
*/
@WebFilter(urlPatterns = "/*")
public class LogFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(LogFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 过滤器初始化操作(可选)
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 记录请求信息
logger.info("请求协议: {}", request.getProtocol());
logger.info("请求 IP: {}", request.getRemoteAddr());
// 继续处理请求
chain.doFilter(request, response);
// 记录响应信息
logger.info("响应类型: {}", response.getContentType());
}
@Override
public void destroy() {
// 过滤器销毁操作(可选)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 4. 关键 API 和参数说明
doFilter
: 过滤器的核心方法,所有的请求和响应都会通过此方法进行拦截。ServletRequest request
: 请求对象,包含了请求协议、IP 地址等信息。ServletResponse response
: 响应对象,包含了响应类型等信息。
结论
- Spring AOP 适合方法级别的日志拦截,可以灵活控制日志输出的内容。
- HandlerInterceptor 适合在控制器层面统一管理日志,使用简单,配置灵活。
- Logback + Filter 适合需要全局日志控制和更细粒度定制的场景。