您对执行机制的洞察非常准确!让我们深入分析这三种组件的调用机制及其与 AOP 节点的关系:
一、执行机制的本质区别
组件 | 调用机制 | 实现原理 |
---|---|---|
Servlet 过滤器 | 递归调用 | 通过 FilterChain.doFilter() 显式递归调用下一个节点 |
Spring MVC 拦截器 | 遍历调用 | 由 HandlerExecutionChain 内部维护列表,顺序遍历执行 |
Spring AOP 拦截器 | 链式递归调用 | 通过 MethodInvocation.proceed() 递归调用下一个拦截器,目标方法作为终点节点 |
二、详细机制解析
1. Servlet 过滤器:显式递归调用
源码调用,通过递归调用
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (Globals.IS_SECURITY_ENABLED) {
ServletRequest req = request;
ServletResponse res = response;
try {
AccessController.doPrivileged(() -> {
this.internalDoFilter(req, res); //调用过滤器链
return null;
});
} catch (PrivilegedActionException var7) {
Exception e = var7.getException();
if (e instanceof ServletException) {
throw (ServletException)e;
}
if (e instanceof IOException) {
throw (IOException)e;
}
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
}
throw new ServletException(e.getMessage(), e);
}
} else {
this.internalDoFilter(request, response);
}
}
private void internalDoFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// 1. 还有过滤器就继续执行
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
filter.doFilter(request, response, this); // 把 this(链本身)传回去,提供给后续过滤器使用,每次调用就会到这个过滤器链。
return;
}
// 2. 没有过滤器了——终点:调 Servlet
servlet.service(request, response);
}
public class MyFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 前置处理 (递归入口)
System.out.println("Filter前处理");
// ▶ 显式递归调用:必须手动触发下一个节点 ◀
chain.doFilter(request, response);
// 后置处理 (递归返回)
System.out.println("Filter后处理");
}
}
调用栈示例:
2. MVC 拦截器:隐式遍历调用
源码调用所有拦截器
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
HandlerExecutionChain mappedHandler = getHandler(request); // 获取执行链
// 1. 前置拦截
if (!mappedHandler.applyPreHandle(request, response)) return;
// 2. 执行业务Controller
ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 3. 后置拦截
mappedHandler.applyPostHandle(request, response, mv);
// 4. 渲染视图后触发afterCompletion
mappedHandler.triggerAfterCompletion(request, response, null);
}
public class HandlerExecutionChain {
private final List<HandlerInterceptor> interceptors = new ArrayList<>();
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) {
// ▶ 顺序遍历执行 preHandle ◀
for (int i = 0; i < this.interceptors.size(); i++) {
if (!interceptors.get(i).preHandle(request, response, this.handler)) {
return false; // 中断
}
}
return true;
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response,
ModelAndView mv) throws Exception {
// ▶ 逆序遍历执行 postHandle ◀
for (int i = this.interceptors.size() - 1; i >= 0; i--) {
interceptors.get(i).postHandle(request, response, this.handler, mv);
}
}
}
AOP增强,本质上也是拦截器MethodInterceptor
一、核心矛盾解析:为什么最后一个节点直接调用业务方法?
源码调用,每次调用将该拦截器链传入进去。ReflectiveMethodInvocation源码,外部使用这个出发拦截器链。
public Object proceed() throws Throwable {
// 检查是否到达链末端
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint(); // 执行业务方法
}
// 获取下一个拦截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// 递归调用下一个拦截器 ▼▼▼ 核心递归点 ▼▼▼
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
二、执行流程全景解析
三、后置增强的逻辑
后置增强并非独立的拦截器节点,而是通过递归返回机制实现的:
1. 拦截器的标准结构
public class MyInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation mi) throws Throwable {
// 前置逻辑
System.out.println("Before business method");
// 关键:递归调用链
Object result = mi.proceed();
// 后置逻辑
System.out.println("After business method");
return result;
}
}
2. 执行栈展开过程
// 伪代码表示执行栈
Stack:
1. MyInterceptor.invoke()
-> 调用 mi.proceed()
2. ReflectiveMethodInvocation.proceed()
-> 调用下一个拦截器
... 递归直到最后一个 ...
N. ReflectiveMethodInvocation.proceed()
-> 执行原始方法
-> 返回结果
// 栈开始展开
N-1. 上一个拦截器收到结果
-> 执行后置逻辑
-> 返回结果
... 递归返回 ...
1. 第一个拦截器收到结果
-> 执行后置逻辑
-> 返回最终结果
四、源码验证:Spring 内置拦截器实现
1. 后置返回通知 (AfterReturningAdviceInterceptor
)
public Object invoke(MethodInvocation mi) throws Throwable {
// 前置:无操作
// 关键:先执行后续链(包含业务方法)
Object retVal = mi.proceed();
// 后置:在业务方法返回后执行
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
2. 最终通知 (AspectJAfterAdvice
)
public Object invoke(MethodInvocation mi) throws Throwable {
try {
// 关键:先执行后续链
return mi.proceed();
} finally {
// 后置:在finally块中确保执行
invokeAdviceMethod();
}
}
执行顺序:
三、拦截器方法与 AOP 节点的关系
1. MVC 拦截器 vs AOP 拦截器
特性 | MVC 拦截器方法 | AOP 拦截节点 |
---|---|---|
执行位置 | preHandle /postHandle |
MethodInterceptor.invoke() |
调用方式 | 遍历调用 | 链式递归调用 |
目标对象 | Controller 方法 | 任意 Spring Bean 方法 |
访问权限 | 只能访问 HTTP 请求/响应 | 能访问方法参数、返回值、目标对象 |
是否共享节点 | ❌ 独立于 AOP 链 | ✅ 是 AOP 链的一部分 |
2. 关键区别图示
graph TB
subgraph HTTP请求流程
Filter[Servlet过滤器] --> Interceptor[MVC拦截器]
Interceptor --> Dispatcher[DispatcherServlet]
end
subgraph Spring处理流程
Dispatcher --> Proxy[Controller代理对象]
Proxy --> AOP[AOP拦截链]
AOP --> Bean[原始Controller方法]
end
note[重要:MVC拦截器在AOP代理之外执行]
四、自定义组件加入位置
1. Servlet 过滤器加入点
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<MyFilter> myFilter() {
FilterRegistrationBean<MyFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new MyFilter());
bean.setOrder(1); // ▶ 关键:通过order控制递归顺序 ◀
bean.addUrlPatterns("/*");
return bean;
}
}
2. MVC 拦截器加入点
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// ▶ 加入遍历列表,按注册顺序执行 ◀
registry.addInterceptor(new AuthInterceptor())
.order(1) // 可选排序
.addPathPatterns("/api/**");
}
}
五、执行流程全景图
六、技术栈分层架构
总结
调用机制本质:
- 过滤器:显式递归调用(必须调用
chain.doFilter()
) - MVC拦截器:隐式遍历调用(框架自动控制)
- AOP拦截器:链式递归调用(通过
proceed()
传递)
- 过滤器:显式递归调用(必须调用
与AOP节点的关系:
- MVC拦截器 不是 AOP链的一部分
- Controller方法的执行会进入AOP代理链
- MVC拦截器在AOP代理链 之前 执行
自定义组件加入:
- 过滤器:通过
FilterRegistrationBean
加入Servlet容器调用链 - MVC拦截器:通过
WebMvcConfigurer
加入Spring MVC遍历列表 - AOP拦截器:通过
@Aspect
加入Spring AOP代理链
- 过滤器:通过
理解这些差异对于构建高效、可维护的Web应用至关重要:
- 使用过滤器处理底层HTTP问题(编码/安全)
- 使用MVC拦截器处理请求生命周期(认证/日志)
- 使用AOP拦截器处理业务逻辑(事务/性能监控)
这种分层设计让每个组件都能在最适合的层级发挥作用,既保持职责分离,又能协同完成复杂请求处理!