spring boot 提供了多种组件来处理请求的不同阶段:过滤器、拦截器、aop、监听器、controlleradvice、参数/返回值处理器。它们功能上有重叠但各有定位,初学者容易混淆。本文系统讲解每种组件的原理、用法,并从执行顺序、适用场景、功能交叉点等维度进行横向对比,帮助在不同场景下做出正确选型。
一、整体架构与执行流程
1.1 请求处理全链路
一个 http 请求到达 spring boot 应用后,会依次经过以下组件:
http 请求
|
v
┌─────────────────────────────────────────────────────────┐
│ servlet 容器(tomcat) │
│ ↓ │
│ filter(过滤器)─── 可拦截请求和响应 │
│ ↓ │
│ dispatcherservlet │
│ ├──→ handlerinterceptor.prehandle()(拦截器-前置) │
│ ├──→ handlermethodargumentresolver(参数处理器) │
│ ├──→ controller 方法(被 aop 环绕) │
│ │ └──→ aop @before / @around │
│ ├──→ handlermethodreturnvaluehandler(返回值处理器) │
│ ├──→ handlerinterceptor.posthandle()(拦截器-后置) │
│ └──→ handlerinterceptor.aftercompletion()(拦截器-完成)│
│ │
│ 异常分支: │
│ 任何阶段抛出异常 → @restcontrolleradvice 捕获 │
│ │
│ 事件分支: │
│ servletrequestlistener / applicationevent 贯穿全程 │
└─────────────────────────────────────────────────────────┘
|
v
http 响应
1.2 六大组件定位
| 组件 | 所属层级 | 核心职责 | 一句话定位 |
|---|---|---|---|
| filter | servlet 容器 | 请求/响应的过滤与包装 | 最外层的守门员 |
| interceptor | spring mvc | controller 前后拦截 | controller 的贴身保镖 |
| aop | spring 容器 | 方法级别的横切逻辑 | 任意 bean 方法的拦截器 |
| listener | servlet / spring | 事件监听与回调 | 全局事件的观察者 |
| controlleradvice | spring mvc | 全局异常处理与数据绑定 | controller 的后勤保障 |
| argumentresolver | spring mvc | 自定义参数解析 | 请求参数 → controller 入参 |
| returnvaluehandler | spring mvc | 自定义返回值处理 | controller 返回值 → 响应 |
二、filter(过滤器)
2.1 原理
filter 是 servlet 规范的一部分,运行在 dispatcherservlet 之前和之后,可以拦截所有 http 请求(包括静态资源)。
请求 → filterchain
filter1.dofilter()
filter2.dofilter()
dispatcherservlet
filter2(响应)
filter1(响应)
响应
2.2 用法
import jakarta.servlet.*;
import jakarta.servlet.http.httpservletrequest;
import jakarta.servlet.http.httpservletresponse;
import org.springframework.core.annotation.order;
import org.springframework.stereotype.component;
import java.io.ioexception;
/**
* 请求日志过滤器
*/
@component
@order(1)
public class requestlogfilter implements filter {
@override
public void dofilter(servletrequest request, servletresponse response,
filterchain chain) throws ioexception, servletexception {
httpservletrequest req = (httpservletrequest) request;
httpservletresponse resp = (httpservletresponse) response;
long start = system.currenttimemillis();
system.out.println("[filter] 请求进入: " + req.getmethod() + " " + req.getrequesturi());
// 放行到下一个 filter 或 dispatcherservlet
chain.dofilter(request, response);
long cost = system.currenttimemillis() - start;
system.out.println("[filter] 请求完成: " + req.getrequesturi() + " 耗时 " + cost + "ms 状态码 " + resp.getstatus());
}
}
也可以通过 filterregistrationbean 注册:
import org.springframework.boot.web.servlet.filterregistrationbean;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
@configuration
public class filterconfig {
@bean
public filterregistrationbean<requestlogfilter> requestlogfilter() {
filterregistrationbean<requestlogfilter> bean = new filterregistrationbean<>();
bean.setfilter(new requestlogfilter());
bean.addurlpatterns("/*");
bean.setorder(1);
bean.setname("requestlogfilter");
return bean;
}
}
2.3 特点
| 特点 | 说明 |
|---|---|
| 拦截范围 | 所有请求(包括静态资源、非 controller 请求) |
| 能否访问 spring bean | 可以(通过注入) |
| 能否获取 controller 信息 | 不能(不知道哪个 controller 处理) |
| 能否修改请求/响应体 | 可以(通过包装 httpservletrequestwrapper) |
| 执行时机 | dispatcherservlet 之前和之后 |
三、interceptor(拦截器)
3.1 原理
interceptor 是 spring mvc 提供的机制,运行在 dispatcherservlet 内部,可以感知到 handlerexecutionchain(即哪个 controller 方法将要执行)。
dispatcherservlet
├── prehandle() → controller 执行前
├── posthandle() → controller 返回后,视图渲染前
└── aftercompletion() → 视图渲染完成后(即使有异常也会执行)
3.2 用法
import jakarta.servlet.http.httpservletrequest;
import jakarta.servlet.http.httpservletresponse;
import org.springframework.stereotype.component;
import org.springframework.web.servlet.handlerinterceptor;
/**
* 登录校验拦截器
*/
@component
public class logininterceptor implements handlerinterceptor {
@override
public boolean prehandle(httpservletrequest request, httpservletresponse response,
object handler) throws exception {
string token = request.getheader("authorization");
if (token == null || token.isempty()) {
response.setstatus(401);
response.getwriter().write("未登录");
return false; // 中断请求
}
system.out.println("[interceptor] prehandle: token=" + token);
return true; // 放行
}
@override
public void posthandle(httpservletrequest request, httpservletresponse response,
object handler, modelandview modelandview) throws exception {
system.out.println("[interceptor] posthandle: controller 执行完毕");
}
@override
public void aftercompletion(httpservletrequest request, httpservletresponse response,
object handler, exception ex) throws exception {
system.out.println("[interceptor] aftercompletion: 请求结束, ex=" + ex);
}
}
注册拦截器并配置拦截路径:
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 webmvcconfig implements webmvcconfigurer {
@override
public void addinterceptors(interceptorregistry registry) {
registry.addinterceptor(new logininterceptor())
.addpathpatterns("/api/**") // 拦截路径
.excludepathpatterns("/api/login"); // 排除路径
}
}
3.3 特点
| 特点 | 说明 |
|---|---|
| 拦截范围 | controller 请求(不拦截静态资源) |
| 能否获取 controller 信息 | 可以(handler 参数包含 handlermethod) |
| 能否中断请求 | 可以(prehandle 返回 false) |
prehandle | controller 执行前,返回 false 中断 |
posthandle | controller 正常返回后执行(抛异常则不执行) |
aftercompletion | 最终执行(无论是否异常) |
注意:posthandle 在 controller 抛出异常时不会执行,但 aftercompletion 一定会执行。
四、aop(面向切面编程)
4.1 原理
aop 是 spring 容器级别的机制,基于动态代理(jdk 或 cglib),可以拦截 spring bean 的方法调用。它不局限于 controller,可以作用于任何 bean。
调用 target.method()
↓
代理对象
├── @before → 方法执行前
├── @afterreturning → 方法正常返回后
├── @afterthrowing → 方法抛出异常后
├── @after → 方法结束后(无论成功失败)
└── @around → 环绕(最强大,可控制是否执行)
4.2 用法
import lombok.extern.slf4j.slf4j;
import org.aspectj.lang.proceedingjoinpoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.component;
/**
* 接口耗时统计切面
*/
@slf4j
@aspect
@component
public class timelogaspect {
/**
* 定义切入点:controller 包下所有 public 方法
*/
@pointcut("execution(public * com.example.demo.controller.*.*(..))")
public void controllerpointcut() {
}
/**
* 环绕通知:统计方法执行耗时
*/
@around("controllerpointcut()")
public object around(proceedingjoinpoint pjp) throws throwable {
long start = system.currenttimemillis();
string methodname = pjp.getsignature().toshortstring();
log.info("[aop] 方法开始: {}", methodname);
try {
object result = pjp.proceed(); // 执行目标方法
long cost = system.currenttimemillis() - start;
log.info("[aop] 方法结束: {}, 耗时 {}ms", methodname, cost);
return result;
} catch (throwable e) {
long cost = system.currenttimemillis() - start;
log.error("[aop] 方法异常: {}, 耗时 {}ms", methodname, cost, e);
throw e;
}
}
/**
* 前置通知
*/
@before("controllerpointcut()")
public void before() {
// 在 @around 的 proceed() 之前执行
}
}
4.3 特点
| 特点 | 说明 |
|---|---|
| 拦截范围 | 任何 spring bean 的方法(不限于 controller) |
| 能否获取方法参数 | 可以(joinpoint.getargs()) |
| 能否修改参数 | 可以(@around 中修改 args 后 proceed) |
| 能否修改返回值 | 可以(@around 中替换返回值) |
| 能否中断执行 | 可以(@around 中不调用 proceed) |
五、listener(监听器)
5.1 原理
监听器是观察者模式的实现,用于监听容器或应用中发生的事件。spring boot 中有两种监听器:
| 类型 | 接口 | 事件 |
|---|---|---|
| servlet 监听器 | servletrequestlistener 等 | 请求创建/销毁 |
| spring 事件监听器 | applicationlistener | 应用启动、刷新、自定义事件 |
5.2 servlet 请求监听器
import jakarta.servlet.servletrequestevent;
import jakarta.servlet.servletrequestlistener;
import jakarta.servlet.annotation.weblistener;
import org.springframework.stereotype.component;
/**
* servlet 请求监听器
*/
@component
public class requestlistener implements servletrequestlistener {
@override
public void requestinitialized(servletrequestevent sre) {
system.out.println("[listener] 请求创建: " + sre.getservletrequest());
}
@override
public void requestdestroyed(servletrequestevent sre) {
system.out.println("[listener] 请求销毁");
}
}
5.3 spring 事件监听器
import org.springframework.context.event.eventlistener;
import org.springframework.stereotype.component;
/**
* spring 事件监听器
*/
@component
public class springeventlistener {
/**
* 监听应用启动完成事件
*/
@eventlistener
public void onapplicationready(applicationreadyevent event) {
system.out.println("[listener] 应用启动完成");
}
/**
* 监听自定义事件
*/
@eventlistener
public void onordercreated(ordercreatedevent event) {
system.out.println("[listener] 收到订单创建事件: " + event.getorderid());
}
}
自定义事件:
import lombok.getter;
import org.springframework.context.applicationevent;
/**
* 订单创建事件
*/
@getter
public class ordercreatedevent extends applicationevent {
private final string orderid;
public ordercreatedevent(object source, string orderid) {
super(source);
this.orderid = orderid;
}
}
发布事件:
import org.springframework.context.applicationeventpublisher;
import org.springframework.stereotype.service;
@service
public class orderservice {
private final applicationeventpublisher eventpublisher;
public orderservice(applicationeventpublisher eventpublisher) {
this.eventpublisher = eventpublisher;
}
public void createorder(string orderid) {
// 业务逻辑...
system.out.println("订单创建: " + orderid);
// 发布事件
eventpublisher.publishevent(new ordercreatedevent(this, orderid));
}
}
与 filter/interceptor 的区别:listener 是被动的事件回调,不能拦截或中断请求流程。
六、controlleradvice(全局异常与数据绑定)
6.1 原理
@restcontrolleradvice / @controlleradvice 是 spring mvc 提供的全局增强机制,可以处理 controller 层的异常、全局数据绑定、全局数据预处理。
6.2 全局异常处理
import lombok.extern.slf4j.slf4j;
import org.springframework.http.httpstatus;
import org.springframework.web.bind.annotation.*;
import java.util.hashmap;
import java.util.map;
/**
* 全局异常处理器
*/
@slf4j
@restcontrolleradvice
public class globalexceptionhandler {
/**
* 处理业务异常
*/
@exceptionhandler(businessexception.class)
@responsestatus(httpstatus.ok)
public map<string, object> handlebusiness(businessexception e) {
log.warn("业务异常: {}", e.getmessage());
map<string, object> result = new hashmap<>();
result.put("code", e.getcode());
result.put("message", e.getmessage());
return result;
}
/**
* 处理参数校验异常
*/
@exceptionhandler(illegalargumentexception.class)
@responsestatus(httpstatus.bad_request)
public map<string, object> handleillegalarg(illegalargumentexception e) {
log.warn("参数异常: {}", e.getmessage());
map<string, object> result = new hashmap<>();
result.put("code", "400");
result.put("message", e.getmessage());
return result;
}
/**
* 兜底:处理所有未捕获异常
*/
@exceptionhandler(exception.class)
@responsestatus(httpstatus.internal_server_error)
public map<string, object> handleexception(exception e) {
log.error("系统异常", e);
map<string, object> result = new hashmap<>();
result.put("code", "500");
result.put("message", "系统繁忙");
return result;
}
}
6.3 全局数据绑定
@restcontrolleradvice
public class globaldatabinder {
@initbinder
public void initbinder(webdatabinder binder) {
// 注册自定义编辑器
binder.registercustomeditor(string.class, new stringtrimmereditor(true));
}
}
6.4 全局数据预处理
@restcontrolleradvice
public class globalrequestbodyadvice {
@modelattribute
public void addcommonattributes(model model) {
model.addattribute("servertime", system.currenttimemillis());
}
}
关键:@exceptionhandler 只能捕获 controller 层抛出的异常,不能捕获 filter 层的异常。
七、handlermethodargumentresolver(参数处理器)
7.1 原理
参数处理器负责将 http 请求中的数据解析为 controller 方法的入参。spring mvc 内置了多个处理器:
| 内置处理器 | 处理的注解 |
|---|---|
requestparammethodargumentresolver | @requestparam |
pathvariablemethodargumentresolver | @pathvariable |
requestbodymethodargumentresolver | @requestbody |
requestheadermethodargumentresolver | @requestheader |
modelattributemethodprocessor | @modelattribute |
当内置处理器不满足需求时,可以自定义。
7.2 自定义参数处理器示例
场景:从 token 中解析当前登录用户,直接注入 controller 方法。
import org.springframework.core.methodparameter;
import org.springframework.stereotype.component;
import org.springframework.web.bind.support.webdatabinderfactory;
import org.springframework.web.context.request.nativewebrequest;
import org.springframework.web.method.support.handlermethodargumentresolver;
import org.springframework.web.method.support.modelandviewcontainer;
import jakarta.servlet.http.httpservletrequest;
/**
* 自定义参数处理器:解析 @currentuser 注解
*/
@component
public class currentuserargumentresolver implements handlermethodargumentresolver {
@override
public boolean supportsparameter(methodparameter parameter) {
return parameter.hasparameterannotation(currentuser.class)
&& parameter.getparametertype().equals(userinfo.class);
}
@override
public object resolveargument(methodparameter parameter,
modelandviewcontainer mavcontainer,
nativewebrequest webrequest,
webdatabinderfactory binderfactory) throws exception {
httpservletrequest request = webrequest.getnativerequest(httpservletrequest.class);
string token = request.getheader("authorization");
// 从 token 解析用户信息(实际项目中通常从 jwt 或缓存中获取)
userinfo user = parsetoken(token);
return user;
}
private userinfo parsetoken(string token) {
userinfo user = new userinfo();
user.setid(1001l);
user.setname("张三");
return user;
}
}
自定义注解:
import java.lang.annotation.*;
@target(elementtype.parameter)
@retention(retentionpolicy.runtime)
@documented
public @interface currentuser {
}
用户信息类:
import lombok.data;
@data
public class userinfo {
private long id;
private string name;
}
注册参数处理器:
import org.springframework.context.annotation.configuration;
import org.springframework.web.method.support.handlermethodargumentresolver;
import org.springframework.web.servlet.config.annotation.webmvcconfigurer;
import java.util.list;
@configuration
public class webmvcconfig implements webmvcconfigurer {
private final currentuserargumentresolver currentuserresolver;
public webmvcconfig(currentuserargumentresolver currentuserresolver) {
this.currentuserresolver = currentuserresolver;
}
@override
public void addargumentresolvers(list<handlermethodargumentresolver> resolvers) {
resolvers.add(currentuserresolver);
}
}
在 controller 中使用:
@restcontroller
@requestmapping("/api/user")
public class usercontroller {
@getmapping("/profile")
public map<string, object> getprofile(@currentuser userinfo user) {
map<string, object> result = new hashmap<>();
result.put("id", user.getid());
result.put("name", user.getname());
return result;
}
}
八、handlermethodreturnvaluehandler(返回值处理器)
8.1 原理
返回值处理器负责将 controller 方法的返回值转换为 http 响应。内置处理器包括:
| 内置处理器 | 处理场景 |
|---|---|
requestresponsebodymethodprocessor | @responsebody / @restcontroller |
modelandviewmethodreturnvaluehandler | 返回 modelandview |
viewmethodreturnvaluehandler | 返回 string(视图名) |
httpentitymethodprocessor | 返回 responseentity |
8.2 自定义返回值处理器示例
场景:controller 方法返回实体对象,自动包装为统一响应格式 result<t>。
import org.springframework.core.methodparameter;
import org.springframework.http.mediatype;
import org.springframework.http.converter.httpmessageconverter;
import org.springframework.http.server.serverhttprequest;
import org.springframework.http.server.serverhttpresponse;
import org.springframework.web.bind.annotation.restcontrolleradvice;
import org.springframework.web.servlet.mvc.method.annotation.responsebodyadvice;
import java.util.hashmap;
import java.util.map;
/**
* 统一返回值包装
* <p>
* 通过 responsebodyadvice 实现,它本质上是返回值处理器的增强钩子。
*/
@restcontrolleradvice
public class globalresponsehandler implements responsebodyadvice<object> {
@override
public boolean supports(methodparameter returntype,
class<? extends httpmessageconverter<?>> convertertype) {
// 如果已经是 result 类型,则不再包装
return !returntype.getparametertype().equals(result.class);
}
@override
public object beforebodywrite(object body,
methodparameter returntype,
mediatype selectedcontenttype,
class<? extends httpmessageconverter<?>> selectedconvertertype,
serverhttprequest request,
serverhttpresponse response) {
map<string, object> result = new hashmap<>();
result.put("code", 0);
result.put("message", "success");
result.put("data", body);
return result;
}
}
统一响应类:
import lombok.data;
@data
public class result<t> {
private int code;
private string message;
private t data;
public static <t> result<t> success(t data) {
result<t> r = new result<>();
r.setcode(0);
r.setmessage("success");
r.setdata(data);
return r;
}
public static <t> result<t> fail(string message) {
result<t> r = new result<>();
r.setcode(500);
r.setmessage(message);
return r;
}
}
注意:responsebodyadvice 不是完整的 returnvaluehandler,而是在 requestresponsebodymethodprocessor 内部的增强钩子。完整自定义需要实现 handlermethodreturnvaluehandler 接口并注册,但实际项目中 responsebodyadvice 已足够。
九、横向对比
9.1 执行顺序
http 请求到达
│
├── 1. listener.requestinitialized() ← servlet 请求监听器
│
├── 2. filter.dofilter() [请求阶段] ← 过滤器
│ (filter1 → filter2 → ...)
│
├── 3. dispatcherservlet
│ │
│ ├── 3.1 interceptor.prehandle() ← 拦截器-前置
│ │
│ ├── 3.2 argumentresolver ← 参数处理器
│ │
│ ├── 3.3 aop @around [前半段] ← aop(如果有)
│ │ ├── aop @before
│ │ ├── controller 方法执行
│ │ └── aop @afterreturning / @afterthrowing
│ │ └── aop @after
│ │ └── aop @around [后半段]
│ │
│ ├── 3.4 returnvaluehandler ← 返回值处理器
│ │ └── responsebodyadvice
│ │
│ ├── 3.5 interceptor.posthandle() ← 拦截器-后置
│ │
│ └── 3.6 interceptor.aftercompletion() ← 拦截器-完成
│
├── 4. filter.dofilter() [响应阶段]
│ (... → filter2 → filter1)
│
└── 5. listener.requestdestroyed() ← servlet 请求监听器
异常分支:
controller 抛出异常
→ aop @afterthrowing
→ interceptor.aftercompletion()
→ @restcontrolleradvice @exceptionhandler
filter 抛出异常
→ 不会进入 controlleradvice(需在 filter 中自行处理)
核心记忆:filter → interceptor.prehandle → argumentresolver → aop → controller → returnvaluehandler → interceptor.posthandle → interceptor.aftercompletion → filter
9.2 功能对比矩阵
| 能力 | filter | interceptor | aop | listener | controlleradvice | argumentresolver | returnvaluehandler |
|---|---|---|---|---|---|---|---|
| 拦截所有请求 | ✅ | ❌ 仅 controller | ❌ 仅 bean 方法 | ✅ | ❌ | ❌ | ❌ |
| 拦截静态资源 | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| 获取 controller 方法信息 | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ |
| 获取方法参数值 | ❌ | ✅ | ✅ | ❌ | ❌ | ✅(可修改) | ❌ |
| 修改方法参数 | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ |
| 修改返回值 | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ |
| 中断请求 | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| 处理异常 | ✅(自行处理) | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ |
| 感知事务 | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| 作用范围 | servlet 容器 | spring mvc | spring 容器 | servlet/spring | spring mvc | spring mvc | spring mvc |
9.3 适用场景对比
| 场景 | 推荐组件 | 理由 |
|---|---|---|
| 请求日志记录 | filter | 能记录所有请求(含静态资源),且在最早阶段拦截 |
| 登录鉴权 | interceptor | 只需拦截 controller 请求,可获取 handler 信息,可排除特定路径 |
| 权限校验 | interceptor | 同上,且可获取 controller 注解 |
| 接口耗时统计 | aop | 可精确到方法级别,可统计 service 层耗时 |
| 参数校验/签名 | aop 或 interceptor | aop 更灵活(可作用于 service 层),interceptor 更简单 |
| 统一返回值包装 | responsebodyadvice | 不侵入 controller 代码,自动包装所有返回值 |
| 自定义参数注入 | argumentresolver | 如 @currentuser 注入登录用户 |
| 全局异常处理 | controlleradvice | 统一处理 controller 异常,避免 try-catch 散落各处 |
| 跨域处理 | filter 或 corsfilter | 在最早阶段处理 cors 预检请求 |
| 请求体加密/解密 | filter + wrapper | 需要在请求到达 controller 前修改请求体 |
| 事件通知 | listener | 异步解耦,如订单创建后发通知 |
| 接口限流 | filter 或 interceptor | filter 更早拦截减少资源消耗 |
9.4 功能交叉点分析
filter vs interceptor — 都能拦截请求
相同点:
- 都能在请求前后执行逻辑
- 都能中断请求
- 都能获取 httpservletrequest / httpservletresponse
不同点:
- filter 拦截所有请求(含静态资源),interceptor 只拦截 controller
- filter 不感知 controller 信息,interceptor 可以获取 handlermethod
- filter 在 dispatcherservlet 外,interceptor 在内
interceptor 的 excludepathpatterns 配置更灵活
选型建议:
- 需要拦截所有请求 → filter(如跨域、字符编码)
- 只需拦截 controller → interceptor(如登录校验、权限控制)
interceptor vs aop — 都能拦截 controller 方法
相同点:
- 都能在 controller 方法前后执行逻辑
- 都能获取方法签名和参数
- 都能中断执行
不同点:
- interceptor 只能拦截 controller,aop 能拦截任何 bean
- interceptor 有 posthandle(视图渲染前),aop 没有
- aop 更灵活(@around 可控制是否执行、可修改参数和返回值)
- interceptor 更简单(实现接口即可,无需切点表达式)
- aop 基于动态代理,同类内部方法调用不走代理(自调用问题)
选型建议:
- 只需拦截 controller、逻辑简单 → interceptor
- 需要拦截 service 层、需要修改参数或返回值 → aop
controlleradvice vs interceptor — 都能处理异常
相同点:都在 spring mvc 层面工作
不同点:
- controlleradvice 专门处理异常,interceptor 的 aftercompletion 只能感知异常但不能处理
- controlleradvice 可以针对不同异常类型返回不同响应
- interceptor 无法修改异常的走向,异常会继续传播
选型建议:异常处理 → 始终用 controlleradvice
filter vs interceptor — 都能做日志记录
filter 做日志:
- 能记录所有请求(含 404、静态资源)
- 能获取最终响应状态码
- 不能获取 controller 类名和方法名
interceptor 做日志:
- 能获取 controller 类名和方法名
- 可以排除不需要记录的路径
- 不能记录非 controller 请求(如 404)
选型建议:
- 访问日志(access log)→ filter
- 接口调用日志(需要方法名)→ interceptor 或 aop
十、实战:组合使用示例
一个典型的 spring boot 项目通常同时使用多种组件:
import org.springframework.context.annotation.configuration;
import org.springframework.web.method.support.handlermethodargumentresolver;
import org.springframework.web.servlet.config.annotation.interceptorregistry;
import org.springframework.web.servlet.config.annotation.webmvcconfigurer;
import java.util.list;
/**
* web mvc 统一配置
*/
@configuration
public class webmvcconfig implements webmvcconfigurer {
private final logininterceptor logininterceptor;
private final currentuserargumentresolver currentuserresolver;
public webmvcconfig(logininterceptor logininterceptor,
currentuserargumentresolver currentuserresolver) {
this.logininterceptor = logininterceptor;
this.currentuserresolver = currentuserresolver;
}
@override
public void addinterceptors(interceptorregistry registry) {
registry.addinterceptor(logininterceptor)
.addpathpatterns("/api/**")
.excludepathpatterns("/api/login", "/api/register");
}
@override
public void addargumentresolvers(list<handlermethodargumentresolver> resolvers) {
resolvers.add(currentuserresolver);
}
}
各组件分工:
| 组件 | 职责 |
|---|---|
| filter | 请求日志、跨域处理、字符编码 |
| interceptor | 登录校验、权限控制 |
| aop | 接口耗时统计、操作日志 |
| controlleradvice | 全局异常处理 |
| argumentresolver | @currentuser 注入登录用户 |
| responsebodyadvice | 统一返回值包装 |
| listener | 应用启动初始化、订单事件通知 |
controller 代码保持极简:
@restcontroller
@requestmapping("/api/order")
public class ordercontroller {
private final orderservice orderservice;
public ordercontroller(orderservice orderservice) {
this.orderservice = orderservice;
}
@postmapping("/create")
public ordervo createorder(@currentuser userinfo user,
@requestbody @valid ordercreaterequest request) {
return orderservice.createorder(user.getid(), request);
}
}
执行流程:
- filter → 记录请求日志
- interceptor → 校验登录 token
- argumentresolver → 解析 token,注入 userinfo
- aop @around → 开始计时
- controller → 执行业务逻辑
- aop @around → 结束计时,记录日志
- responsebodyadvice → 包装返回值为 {code, message, data}
- interceptor.aftercompletion → 清理资源
- filter → 记录响应日志
十一、常见误区与最佳实践
11.1 常见误区
| 误区 | 正解 |
|---|---|
| filter 中抛异常会被 controlleradvice 捕获 | ❌ 不会。filter 在 dispatcherservlet 之外,需要自行 try-catch |
| interceptor 的 posthandle 在异常时也会执行 | ❌ controller 抛异常时 posthandle 不执行,只有 aftercompletion 执行 |
| aop 可以拦截同类内部方法调用 | ❌ this.method() 不走代理,只有外部调用才走代理 |
| listener 可以中断请求 | ❌ listener 是事件回调,不能中断流程 |
| responsebodyadvice 对所有返回值生效 | ❌ 只对 @responsebody / @restcontroller 的方法生效 |
| 多个 interceptor 的 posthandle 按注册顺序执行 | ❌ posthandle 和 aftercompletion 按注册的逆序执行 |
11.2 多个拦截器的执行顺序
拦截器注册顺序:a → b → c
prehandle 执行顺序:a → b → c(正序)
posthandle 执行顺序:c → b → a(逆序)
aftercompletion 执行顺序:c → b → a(逆序)
如果 b 的 prehandle 返回 false:
a.prehandle ✅
b.prehandle ❌(返回 false)
c.prehandle 不执行
controller 不执行
posthandle 都不执行
aftercompletion:a.aftercompletion ✅(已 prehandle 成功的会执行)
c.aftercompletion 不执行
11.3 最佳实践
- 职责单一:每个组件只做一件事,不要在 filter 中做业务逻辑
- 异常处理分层:filter 异常自行处理,controller 异常交给 controlleradvice
- 避免循环依赖:filter 中注入 bean 时注意 bean 的初始化时机
- 拦截器路径配置:用
excludepathpatterns排除登录、注册等公开接口 - aop 自调用问题:同类方法内部调用不走代理,需要通过
aopcontext.currentproxy()或拆分到不同类 - 参数处理器优先级:自定义 argumentresolver 添加到列表末尾,避免覆盖内置处理器
- responsebodyadvice 判断类型:在
supports()中排除已是 result 类型的返回值,避免重复包装
十二、总结
12.1 选型决策树
需要在请求最外层拦截?
├── 是 → filter
│ (跨域、编码、访问日志)
└── 否 → 需要拦截 controller 方法?
├── 是 → 需要修改参数或返回值?
│ ├── 是 → aop
│ │ (耗时统计、参数处理)
│ └── 否 → 需要获取 handler 信息?
│ ├── 是 → interceptor
│ │ (登录校验、权限控制)
│ └── 否 → aop 或 interceptor 均可
└── 否 → 需要处理异常?
├── 是 → controlleradvice
│ (全局异常处理)
└── 否 → 需要自定义参数注入?
├── 是 → argumentresolver
│ (@currentuser)
└── 否 → 需要统一包装返回值?
├── 是 → responsebodyadvice
└── 否 → 需要监听事件?
└── listener
12.2 核心要点速查
- filter 在 dispatcherservlet 外,拦截所有请求,适合跨域、编码、访问日志
- interceptor 在 dispatcherservlet 内,只拦截 controller,适合登录鉴权、权限控制
- aop 基于 spring 动态代理,能拦截任何 bean 方法,适合耗时统计、操作日志
- listener 是事件回调,不能中断请求,适合应用初始化、事件通知
- controlleradvice 专门处理 controller 层异常,不能捕获 filter 层异常
- argumentresolver 自定义参数解析,如
@currentuser注入登录用户 - responsebodyadvice 统一包装返回值,不侵入 controller 代码
- 执行顺序:filter → interceptor.prehandle → argumentresolver → aop → controller → returnvaluehandler → interceptor.posthandle → aftercompletion → filter
- interceptor 的 posthandle 和 aftercompletion 按注册逆序执行
- aop 自调用不走代理,需通过
aopcontext.currentproxy()解决
以上就是springboot中请求处理组件全面对比与选型指南的详细内容,更多关于springboot请求处理组件的资料请关注代码网其它相关文章!
发表评论