责任链模式:Servlet Filter与Spring Interceptor的链式执行原理
责任链模式:Servlet Filter与Spring Interceptor的链式执行原理
适读人群:中高级Java开发者 | 阅读时长:约22分钟 | 模式类型:行为型
开篇故事
做 API 网关开发时,我遇到了一个典型问题:每个 API 请求进来,都需要经过一系列的处理步骤——认证(Authentication)、鉴权(Authorization)、限流、日志记录、请求参数校验、响应体加密……
最开始,这些逻辑都堆在一个 GatewayFilter 里,几千行代码,每次改动都要小心翼翼,生怕改出问题。而且新增一个处理步骤,就要在这个庞大的 Filter 里找到合适的位置插入代码,稍有不慎就会影响到其他步骤的逻辑。
后来用责任链模式重构,每个处理步骤独立成一个 FilterHandler,链式组装,清晰得多。新增步骤只需要实现 FilterHandler 接口并配置到链中,完全不影响已有逻辑。这个重构让代码可维护性提升了一个量级。
而这正是 Servlet Filter 和 Spring Interceptor 的底层设计思路——它们都是责任链模式的典型实现。
一、模式动机:将请求处理解耦成处理链
责任链模式(Chain of Responsibility Pattern):将请求的发送者和接收者解耦,通过让多个处理对象都有机会处理请求,将这些对象连成一条链,并将请求沿链传递,直到有一个对象处理它为止。
两种传递方式:
- 中断型:某个处理者可以"吞掉"请求,后续处理者不再执行(Servlet Filter 的
chain.doFilter()不调用就中断) - 传递型:所有处理者都会处理,不能中断(Spring Interceptor 的
preHandle()返回 false 可以中断)
二、模式结构
三、Servlet Filter 与 Spring Interceptor 的源码分析
3.1 Servlet Filter 的责任链实现
Servlet 规范中,FilterChain 是责任链的核心接口:
// Servlet的FilterChain(责任链管理者)
public interface FilterChain {
void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
}
// ApplicationFilterChain(Tomcat中的具体实现)
final class ApplicationFilterChain implements FilterChain, DispatcherType {
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0]; // Filter数组
private int pos = 0; // 当前执行到的Filter索引
private int n = 0; // Filter总数
private Servlet servlet = null; // 最终的Servlet
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
internalDoFilter(request, response);
}
private void internalDoFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (pos < n) {
// 还有Filter未执行,取出下一个
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
// 调用Filter的doFilter,传入当前的FilterChain
filter.doFilter(request, response, this);
// 关键:Filter内部调用chain.doFilter()时,
// 实际上又回到了这个方法(pos已经+1了),执行下一个Filter
} catch (IOException | ServletException | RuntimeException e) {
throw e;
}
return;
}
// 所有Filter都执行完了,执行最终的Servlet
servlet.service(request, response);
}
}每个 Filter 的标准写法:
// Filter实现(每个Filter是责任链中的一个节点)
public class AuthenticationFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 前置处理:认证
if (!authenticate(httpRequest)) {
((HttpServletResponse) response).sendError(HttpStatus.UNAUTHORIZED.value());
return; // 不调用chain.doFilter,中断责任链
}
// 传递给下一个处理者(继续责任链)
chain.doFilter(request, response);
// 后置处理(response已经被填充)
logResponseStatus((HttpServletResponse) response);
}
}3.2 Spring MVC HandlerInterceptor
// Spring MVC 拦截器接口
public interface HandlerInterceptor {
// preHandle 返回 false 时,中断责任链
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
// postHandle:Handler执行后,视图渲染前
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {}
// afterCompletion:整个请求完成后(包括视图渲染),用于资源清理
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {}
}Interceptor 的执行顺序(与 Filter 不同,是"围绕 Handler"的):
preHandle[拦截器1] → preHandle[拦截器2] → Handler执行
→ postHandle[拦截器2] → postHandle[拦截器1] (倒序)
→ afterCompletion[拦截器2] → afterCompletion[拦截器1] (倒序)四、生产级代码实现:API 网关请求处理链
4.1 完整的责任链实现
/**
* 请求处理上下文(在整个处理链中流转)
*/
@Data
@Builder
public class RequestContext {
private HttpServletRequest request;
private HttpServletResponse response;
private String requestId;
private Long userId;
private String clientIp;
private Map<String, Object> attributes;
private long startTime;
// 是否已经完成(某个处理器直接返回了响应)
private boolean completed;
public void setAttribute(String key, Object value) {
if (attributes == null) attributes = new HashMap<>();
attributes.put(key, value);
}
@SuppressWarnings("unchecked")
public <T> T getAttribute(String key) {
return attributes != null ? (T) attributes.get(key) : null;
}
}
/**
* 请求处理器接口(Handler)
*/
public interface RequestHandler {
/**
* 处理请求
* @return true 表示继续传递到下一个处理器;false 表示中断(已直接响应)
*/
boolean handle(RequestContext context, HandlerChain chain) throws Exception;
/**
* 处理器名称(用于日志和调试)
*/
String getName();
/**
* 是否跳过某个路径(路径白名单)
*/
default boolean shouldSkip(String path) { return false; }
}
/**
* 处理器链(责任链管理者)
*/
public class HandlerChain {
private final List<RequestHandler> handlers;
private int index = 0;
public HandlerChain(List<RequestHandler> handlers) {
this.handlers = new ArrayList<>(handlers);
}
/**
* 执行下一个处理器
*/
public void proceed(RequestContext context) throws Exception {
while (index < handlers.size()) {
RequestHandler handler = handlers.get(index++);
// 检查白名单
String path = context.getRequest().getRequestURI();
if (handler.shouldSkip(path)) {
continue;
}
boolean continued = handler.handle(context, this);
if (!continued || context.isCompleted()) {
return; // 中断链
}
}
}
}
/**
* 请求ID生成处理器(第1个)
*/
@Component
@Order(1)
@Slf4j
public class RequestIdHandler implements RequestHandler {
@Override
public boolean handle(RequestContext context, HandlerChain chain) throws Exception {
String requestId = context.getRequest().getHeader("X-Request-Id");
if (StringUtils.isEmpty(requestId)) {
requestId = UUID.randomUUID().toString().replace("-", "");
}
context.setRequestId(requestId);
context.getResponse().setHeader("X-Request-Id", requestId);
MDC.put("requestId", requestId); // 放入日志上下文
try {
chain.proceed(context); // 继续传递
} finally {
MDC.remove("requestId"); // 清理日志上下文
}
return true;
}
@Override
public String getName() { return "RequestIdHandler"; }
}
/**
* 认证处理器(第2个)
*/
@Component
@Order(2)
@Slf4j
public class AuthenticationHandler implements RequestHandler {
@Autowired
private JwtTokenService jwtTokenService;
private static final Set<String> WHITE_LIST = Set.of(
"/api/auth/login", "/api/auth/register", "/api/health"
);
@Override
public boolean handle(RequestContext context, HandlerChain chain) throws Exception {
HttpServletRequest request = context.getRequest();
String authHeader = request.getHeader("Authorization");
if (StringUtils.isEmpty(authHeader) || !authHeader.startsWith("Bearer ")) {
writeUnauthorizedResponse(context, "Missing or invalid Authorization header");
return false; // 中断链,已直接返回错误响应
}
String token = authHeader.substring(7);
try {
Long userId = jwtTokenService.validateAndGetUserId(token);
context.setUserId(userId);
context.setAttribute("token", token);
} catch (JwtExpiredException e) {
writeUnauthorizedResponse(context, "Token expired");
return false;
} catch (JwtInvalidException e) {
writeUnauthorizedResponse(context, "Invalid token");
return false;
}
chain.proceed(context);
return true;
}
@Override
public boolean shouldSkip(String path) {
return WHITE_LIST.contains(path);
}
@Override
public String getName() { return "AuthenticationHandler"; }
private void writeUnauthorizedResponse(RequestContext context, String message) throws IOException {
HttpServletResponse response = context.getResponse();
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write(JsonUtils.toJson(ApiResult.error("401", message)));
context.setCompleted(true);
}
}
/**
* 限流处理器(第3个)
*/
@Component
@Order(3)
@Slf4j
public class RateLimitingHandler implements RequestHandler {
@Autowired
private RedissonClient redissonClient;
private static final int MAX_REQUESTS_PER_MINUTE = 60;
@Override
public boolean handle(RequestContext context, HandlerChain chain) throws Exception {
String userId = String.valueOf(context.getUserId());
String key = "rate_limit:user:" + userId;
RAtomicLong counter = redissonClient.getAtomicLong(key);
long current = counter.incrementAndGet();
if (current == 1) {
counter.expire(Duration.ofMinutes(1)); // 第一次请求时设置1分钟过期
}
if (current > MAX_REQUESTS_PER_MINUTE) {
log.warn("Rate limit exceeded for user: {}, count: {}", userId, current);
HttpServletResponse response = context.getResponse();
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.getWriter().write(JsonUtils.toJson(
ApiResult.error("429", "Too many requests, please retry later")
));
context.setCompleted(true);
return false;
}
chain.proceed(context);
return true;
}
@Override
public String getName() { return "RateLimitingHandler"; }
}
/**
* 请求日志处理器(第4个)
*/
@Component
@Order(4)
@Slf4j
public class RequestLoggingHandler implements RequestHandler {
@Override
public boolean handle(RequestContext context, HandlerChain chain) throws Exception {
HttpServletRequest request = context.getRequest();
long startTime = System.currentTimeMillis();
log.info("[{}] {} {} user:{}",
context.getRequestId(), request.getMethod(),
request.getRequestURI(), context.getUserId());
chain.proceed(context);
long elapsed = System.currentTimeMillis() - startTime;
log.info("[{}] completed in {}ms, status: {}",
context.getRequestId(), elapsed, context.getResponse().getStatus());
return true;
}
@Override
public String getName() { return "RequestLoggingHandler"; }
}
/**
* 网关处理链组装(Spring Filter)
*/
@Component
@Slf4j
public class GatewayFilter implements Filter {
@Autowired
private List<RequestHandler> handlers;
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// 按Order排序处理器
List<RequestHandler> sortedHandlers = handlers.stream()
.sorted(Comparator.comparingInt(h -> getOrder(h.getClass())))
.collect(Collectors.toList());
RequestContext context = RequestContext.builder()
.request(request)
.response(response)
.startTime(System.currentTimeMillis())
.attributes(new HashMap<>())
.build();
HandlerChain chain = new HandlerChain(sortedHandlers);
try {
chain.proceed(context);
// 如果处理链没有中断,继续到Servlet
if (!context.isCompleted()) {
filterChain.doFilter(request, response);
}
} catch (Exception e) {
log.error("[{}] Unhandled exception in handler chain: {}",
context.getRequestId(), e.getMessage(), e);
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.getWriter().write(JsonUtils.toJson(ApiResult.error("500", "Internal server error")));
}
}
private int getOrder(Class<?> clazz) {
Order order = clazz.getAnnotation(Order.class);
return order != null ? order.value() : Integer.MAX_VALUE;
}
@Override
public void init(FilterConfig filterConfig) {}
@Override
public void destroy() {}
}五、踩坑实录
坑一:Filter 和 Interceptor 的执行顺序搞混
Filter 在 Servlet 容器层,Interceptor 在 Spring MVC 层。执行顺序是: Filter.doFilter()前置 → Interceptor.preHandle() → Controller方法 → Interceptor.postHandle() → Filter.doFilter()后置 → Interceptor.afterCompletion()
当我在 Filter 里访问 Spring 的 SecurityContext,有时会发现为 null,因为 Security 相关的 Interceptor 还没有执行。正确做法:需要访问 Spring 安全上下文的处理,放在 Interceptor 里,不要放在 Filter 里。
坑二:异常在 Filter 中被吞掉
在 Filter 链中,如果某个 Filter 内部发生了异常但没有向上传播(被 catch 后只打了日志),后续的 Filter 仍然会执行,但请求状态可能已经不正确。要确保关键的处理(比如认证)失败后能正确中断链。
坑三:afterCompletion 中的异常会覆盖原始异常
如果 Controller 方法抛出了异常 A,然后在 afterCompletion 中又抛出了异常 B,异常 A 会被吞掉,只有异常 B 被处理。afterCompletion 中必须做 try-catch,保证不向外抛出异常。
六、总结
责任链模式是处理"需要经过多个处理步骤"请求的最优方案,Servlet Filter 和 Spring Interceptor 都是其工业级实现。
核心设计要点:
- 每个处理器单一职责:一个处理器只做一件事(认证、限流、日志)。
- 处理器之间通过上下文传递数据:不要在处理器之间直接调用,通过
RequestContext共享数据。 - 处理顺序很重要:认证必须在鉴权之前,日志最好包裹整个链(最外层或最内层)。
- 可以中断,也可以传递:根据业务需要决定是"所有处理器都执行"还是"某个处理器可以终止链"。
