Spring——Spring开发实战经验(4)
摘要
本文深入探讨了 Spring 应用中 Interceptor(拦截器)、Filter(过滤器)和 Aspect(切面)的执行顺序、职责及典型使用场景。Filter 是 Servlet 级别的机制,主要用于日志记录、权限验证等,它在请求到达 Spring MVC 的 DispatcherServlet 前执行,响应返回时按配置顺序反向执行。Interceptor 是 Spring 提供的机制,用于拦截 Spring MVC 请求,它在请求进入 Spring MVC 处理之前、Controller 方法执行之前和之后执行。Aspect(AOP)则用于处理横切关注点,如事务管理、日志记录等,它通过代理机制在目标方法执行前后插入额外逻辑。文中还提供了 WebMvcConfig 配置示例、拦截器与其他组件的对比以及拦截器与 AOP 的区别和联系等内容,帮助读者更好地理解和使用这些机制。
1. Interceptor/Filter/Aspect 的执行顺序
在 Spring 应用中,Interceptor
、Filter
和 Aspect
都是用来处理请求的不同机制。它们各自有不同的职责和执行顺序。理解它们的执行顺序对于调试和优化应用非常重要。
1.1. Filter(过滤器)
职责:Filter
是一种 Servlet 级别的机制,主要用于请求和响应的预处理和后处理。它通常用于日志记录、权限验证、请求修改等操作。
执行顺序:Filter
在 Servlet 容器中非常早地被调用,并且它在请求到达 Spring MVC 的处理器之前执行。
执行顺序:
- 请求到达:Filter 在请求到达 Spring MVC 的 DispatcherServlet 前执行。
- Filter 执行:所有配置的
Filter
按照配置顺序依次执行。 - 请求交给 DispatcherServlet:Filter 在请求到达 DispatcherServlet 时,会允许请求继续向下传递。
- 响应返回:响应从 DispatcherServlet 返回时,Filter 会按配置顺序反向执行(从最后一个 Filter 到第一个 Filter)。
典型使用场景:
- 日志记录
- 权限校验
- 请求数据修改
- 响应数据修改
1.2. Interceptor(拦截器)
- 职责:
Interceptor
是 Spring 提供的机制,它用于拦截 Spring MVC 请求。与 Filter 不同的是,Interceptor
是基于 Spring 的控制器和 HandlerMapping 的,并且它只与 Spring MVC 控制器交互。 - 执行顺序:
Interceptor
在请求进入 Spring MVC 处理之前、Controller 方法执行之前和之后执行。
执行顺序:
- 请求到达 DispatcherServlet:当请求到达 DispatcherServlet 时,
Interceptor
会按配置顺序依次执行。 - 执行 Controller 方法:
Interceptor
执行完成后,DispatcherServlet 会调用 Controller 层的处理方法。 - 响应返回:当 Controller 方法返回结果后,
Interceptor
会按配置顺序反向执行,直到最后一个Interceptor
。
典型使用场景:
- 权限验证
- 请求计时
- 处理请求日志
3. Aspect (AOP)(切面)
- 职责:
Aspect
是基于切面编程(AOP)机制的,用于在方法执行前后增加额外的行为(如日志记录、事务处理等)。它是通过@Aspect
注解和切点(@Before
、@After
、@Around
等)来定义的。 - 执行顺序:
Aspect
是 Spring AOP 的一部分,AOP 执行顺序通常是基于方法执行的前后,而不是请求/响应的生命周期。它通常是在 Controller 方法执行前后,或者 Service/DAO 层方法执行前后 进行拦截。
执行顺序:
- 请求到达 DispatcherServlet:在这个阶段,
Interceptor
和Filter
会先执行。 - Controller 方法执行:
Aspect
会在方法执行前后执行,具体的顺序取决于通知的类型(例如@Before
、@After
、@Around
等)。
-
@Before
:在目标方法执行前执行。@After
:在目标方法执行后执行。@Around
:可以在方法执行前后都执行。
- 响应返回:响应返回时,
Aspect
会根据@After
或@AfterReturning
等通知继续执行。
典型使用场景:
- 日志记录
- 事务管理
- 安全校验
执行顺序总结:
阶段 | Filter | Interceptor | Aspect |
请求到达 DispatcherServlet | 首先执行(Filter Chain) | 最后执行(Spring 容器配置的顺序) | 不影响请求进入 DispatcherServlet |
Controller 方法执行前 | 不参与 | 执行前通知 ( ) | 执行前通知 ( 或 ) |
Controller 方法执行后 | 不参与 | 执行后通知 ( ) | 执行后通知 ( 或 ) |
响应返回 | 反向执行(Filter Chain) | 反向执行(Spring 容器配置的顺序) | 执行后通知 ( 或 ) |
执行顺序图解:
- Filter → Interceptor → Controller 方法 → Interceptor → Filter (请求方向)
- Filter ← Interceptor ← Controller 方法 ← Interceptor ← Filter (响应方向)
实用场景示例:
假设有如下情形:
- 你需要在每个请求的最前面进行身份验证,可以使用 Filter 来实现。
- 你需要在控制器方法执行之前和之后记录日志或进行权限校验,可以使用 Interceptor 来实现。
- 你需要在特定方法执行前后做日志记录、事务管理等操作,可以使用 Aspect 来实现。
补充说明:
- Filter 是 Java EE 标准的机制,直接作用于 Servlet 容器,独立于 Spring。
- Interceptor 是 Spring 特有的功能,作用于 Spring MVC 处理的请求。
- Aspect 是 Spring AOP 提供的功能,可以用来处理横切关注点,适用于方法级别的增强。
通过合理的使用 Filter
、Interceptor
和 Aspect
,你可以实现非常灵活的请求处理、日志记录、安全校验等功能。
2. WebMvcConfig配置示例
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Resource
private PermissionInterceptor permissionInterceptor;
@Resource
private CookieInterceptor cookieInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(permissionInterceptor).addPathPatterns("/**");
registry.addInterceptor(cookieInterceptor).addPathPatterns("/**");
}
}
这是一个 Spring MVC 的配置类,WebMvcConfig
实现了 WebMvcConfigurer
接口,主要用于配置和定制 Spring MVC 的行为。以下是代码中的关键点:
@Configuration
:标注该类为一个配置类,Spring 容器会将其识别为 Bean 定义的来源。WebMvcConfigurer
:这是一个 Spring 提供的接口,允许我们通过实现其方法来自定义 Spring MVC 的配置。@Resource
:用于注入 Spring 容器中的 Bean。这里注入了两个拦截器:permissionInterceptor
:权限拦截器,用于处理与用户权限相关的逻辑。cookieInterceptor
:Cookie 拦截器,用于处理与 Cookie 验证或操作相关的逻辑。addInterceptors
方法:这个的是实现WebMvcConfigurer的方法。用来向 Spring MVC 的拦截器链中添加拦截器。InterceptorRegistry
:拦截器注册器,用于配置拦截器及其作用路径。addInterceptor(permissionInterceptor).addPathPatterns("/**")
:- 将
permissionInterceptor
注册到拦截器链。 addPathPatterns("/**")
表示拦截所有请求路径。addInterceptor(cookieInterceptor).addPathPatterns("/**")
:同样注册cookieInterceptor
拦截所有请求路径。
拦截器执行顺序:拦截器会按照注册顺序依次执行(即先执行 permissionInterceptor
,再执行 cookieInterceptor
)。
package org.springframework.web.servlet.config.annotation;
import java.util.List;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
public interface WebMvcConfigurer {
default void configurePathMatch(PathMatchConfigurer configurer) {
}
default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}
default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}
default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}
default void addFormatters(FormatterRegistry registry) {
}
default void addInterceptors(InterceptorRegistry registry) {
}
default void addResourceHandlers(ResourceHandlerRegistry registry) {
}
default void addCorsMappings(CorsRegistry registry) {
}
default void addViewControllers(ViewControllerRegistry registry) {
}
default void configureViewResolvers(ViewResolverRegistry registry) {
}
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}
default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
}
default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}
default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
@Nullable
default Validator getValidator() {
return null;
}
@Nullable
default MessageCodesResolver getMessageCodesResolver() {
return null;
}
}
这段代码定义了 WebMvcConfigurer 接口,它是 Spring MVC 配置的核心接口之一。通过实现这个接口,开发者可以自定义 Spring MVC 的各种行为,包括路径匹配、内容协商、异步支持、默认 Servlet 处理、格式化器、拦截器、资源处理器、跨域映射、视图控制器、视图解析器、参数解析器、返回值处理器、消息转换器、异常处理器以及验证器和消息代码解析器。
3. Spring 中拦截器
Spring MVC的拦截器 (HandlerInterceptor
) 是一种 AOP(面向切面编程) 的实现机制,允许在请求处理的不同阶段拦截 HTTP 请求并执行特定逻辑。以下是拦截器的主要作用:
3.1. 拦截器工作流程
- 触发时机:
-
- 拦截器在请求到达控制器(
@Controller
或@RestController
)之前执行。 - 响应生成后,在发送给客户端之前再次执行。
- 拦截器在请求到达控制器(
- 主要方法:
-
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
:
-
-
- 在请求到达控制器之前执行。
- 返回
true
表示继续处理请求;返回false
表示中止请求。
-
-
postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
:
-
-
- 在控制器处理完请求后,但尚未生成响应之前执行。
-
-
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
:
-
-
- 在视图渲染完成后执行,用于资源清理或记录日志。
-
3.2. 拦截器常见应用场景
- 权限校验(如
PermissionInterceptor
):
-
- 判断用户是否有权限访问某些接口。
- 如果没有权限,直接返回错误响应(如 403 Forbidden)。
- 认证与登录验证:
-
- 验证用户是否已登录,检查
Session
或Token
。 - 如果未登录,跳转到登录页面或返回 401 Unauthorized。
- 验证用户是否已登录,检查
- 请求日志记录:
-
- 记录请求的参数、响应结果、执行时间等信息,用于监控和分析。
- 参数校验与数据预处理:
-
- 检查请求中的参数是否合法。
- 为请求添加必要的上下文数据(如用户信息、请求头补充)。
- 跨站请求伪造(CSRF)防护:
-
- 检查请求头中的 CSRF Token 是否有效。
- 请求追踪与调试:
-
- 生成唯一的请求 ID,用于追踪请求链路。
3.3. 拦截器与其他组件的对比
特性 | 拦截器 (Interceptor) | 过滤器 (Filter) | 切面 (AOP Aspect) |
触发阶段 | Spring MVC 的处理流程内 | Servlet 请求的早期阶段 | 方法级别(如 方法调用) |
关注点 | 与请求处理相关(权限、认证等) | 全局请求过滤(编码、跨域等) | 与业务逻辑相关(如日志、事务管理) |
实现接口 |
|
|
(基于 Spring AOP) |
应用场景 | 请求认证、权限校验、日志记录等 | 请求过滤、跨域、编码设置等 | 日志、事务处理、通用逻辑 |
4. 拦截器与AOP区别
拦截器(Interceptor
)虽然与 AOP(面向切面编程) 有相似之处,但严格来说,拦截器并不是 AOP 的直接实现,而是一个独立的机制。二者有不同的设计目的和实现方式,但在某些场景下确实可以通过拦截器实现类似于 AOP 的功能。下面详细分析它们的区别和联系。
4.1. 拦截器与 AOP 的区别
4.1.1. 核心概念不同
- AOP(Aspect-Oriented Programming):
-
- 是一种面向切面编程的设计思想,用于在不改变核心业务逻辑的情况下,动态地为方法添加额外行为。
- 通过 切面(Aspect) 和 切入点(Pointcut) 的定义,精准定位到某些方法调用,并在方法执行的 前后或异常时 添加逻辑。
- 主要基于 Spring AOP(代理模式) 或 AspectJ(字节码增强)。
- 拦截器(Interceptor):
-
- 是 Spring MVC 或其他框架提供的一种机制,用于对 HTTP 请求和响应 进行拦截和处理。
- 通常应用在 Web 请求的处理链路 中,比如在请求到达控制器之前,或响应生成之后添加逻辑。
4.1.2. 工作范围不同
- AOP:
-
- 应用于方法级别,主要是对业务方法(如
@Service
、@Controller
)进行增强。 - 可以在任何类的方法上进行增强,甚至独立于 Spring MVC。
- 应用于方法级别,主要是对业务方法(如
- 拦截器:
-
- 专注于处理 HTTP 请求和响应,属于 Spring MVC 流程 的一部分。
- 作用范围是 Controller(控制器)方法之前和之后。
4.1.3. 触发机制不同
- AOP:
-
- 基于代理模式(JDK 动态代理或 CGLIB 动态代理)或字节码操作(如 AspectJ)。
- 使用切面定义,依赖切入点表达式(如
execution()
)。 - 仅对特定的方法调用生效,与 HTTP 请求无关。
- 拦截器:
-
- 工作在 Spring MVC 的处理链中,由
DispatcherServlet
调度执行。 - 对 HTTP 请求进行预处理(如认证、日志、跨域检查等)。
- 工作在 Spring MVC 的处理链中,由
4.2. 拦截器是否可以实现 AOP 的功能?
在某些场景下,拦截器确实可以实现类似 AOP 的功能,例如:
- 记录日志: 在拦截器的
preHandle
方法中记录请求的参数。 - 权限校验: 在拦截器中判断是否有访问权限,阻止不合法请求。
但拦截器的能力有限:
- 它只能拦截基于 HTTP 的请求,无法对方法调用(非 HTTP 场景)进行增强。
- 它无法精准到某个类或方法,而是基于请求路径。
AOP 更灵活和强大,适合以下场景:
- 精准增强特定方法(通过切入点)。
- 跨服务的通用逻辑(如事务管理、性能监控)。
4.3. 拦截器与 AOP 的联系
- 二者的功能在某些场景下重叠。例如,拦截器可以在请求进入 Controller 前后,记录日志或进行校验,这与 AOP 的切面功能相似。
- 在 Spring 应用中,AOP 和拦截器经常配合使用:
-
- 拦截器处理全局的 HTTP 请求级别逻辑。
- AOP 处理业务方法级别的切面逻辑。
4.4. 拦截器和 AOP 的对比表
特性 | AOP | 拦截器(Interceptor) |
作用范围 | 方法级别(类、方法的调用) | 请求级别(HTTP 请求前后) |
依赖机制 | 动态代理(JDK Proxy 或 CGLIB) | Spring MVC 的 |
主要应用场景 | 日志、事务管理、性能监控、方法增强等 | 请求认证、权限校验、跨域、统一日志等 |
触发点 | 方法调用(如 方法) | HTTP 请求的处理过程 |
实现方式 |
(Spring AOP 或 AspectJ) | 实现 接口 |
局限性 | 无法直接作用于 HTTP 请求 | 无法精确到特定方法,强依赖 Spring MVC |
4.5. 示例对比
4.5.1. AOP 切面示例
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
@Before("serviceMethods()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
}
- 作用: 在
com.example.service
包中的所有方法执行之前记录日志。
4.5.2. 拦截器示例
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("Before handling request: " + request.getRequestURI());
return true;
}
}
- 作用: 在每次 HTTP 请求到达 Controller 之前记录请求路径。
4.6. 总结
- 拦截器不是 AOP 的实现,但它们的功能在某些场景下有重叠。
- 拦截器 更偏向于 HTTP 请求级别的拦截,而 AOP 更灵活,适合方法级增强。
- 在复杂项目中,拦截器和 AOP 通常配合使用,实现从请求级别到方法级别的逻辑分离。