拦截器设计模式:用SpringBoot3实现优雅的业务解耦
在当今快速迭代的互联网应用中,如何保持代码的可维护性和扩展性一直是架构师面临的核心挑战。想象这样一个场景:你的电商系统需要在用户下单前进行风险控制检查,在支付完成后自动发送通知,同时还要对敏感数据进行实时脱敏处理——这些横切关注点如果直接嵌入业务代码,很快就会让系统变成难以维护的"意大利面条式代码"。这正是拦截器模式大显身手的时刻。
1. 拦截器模式的核心价值
拦截器(Interceptor)作为经典的AOP实现方式,其本质是在执行流程的关键节点插入处理逻辑,而不需要修改原有代码。与直接修改业务代码相比,这种模式带来了三个显著优势:
- 关注点分离:将认证、日志、监控等非业务逻辑从业务代码中剥离
- 动态增强:无需修改源代码即可添加或移除功能
- 执行控制:通过返回值决定是否继续执行后续流程
在Spring MVC体系中,拦截器通过HandlerInterceptor接口实现,其生命周期包含三个关键阶段:
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {}
}
2. SpringBoot3中的拦截器实战
2.1 基础实现步骤
在SpringBoot3中实现一个完整的拦截器需要三个核心组件:
- 拦截器实现类:继承
HandlerInterceptor接口 - 配置类:实现
WebMvcConfigurer接口 - 控制器:提供被拦截的端点
以下是一个完整的权限校验拦截器示例:
// 拦截器实现
@Component
public class AuthInterceptor implements HandlerInterceptor {
private final AuthService authService;
public AuthInterceptor(AuthService authService) {
this.authService = authService;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader("Authorization");
if (!authService.validateToken(token)) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false; // 中断请求
}
return true;
}
}
// 配置类
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final AuthInterceptor authInterceptor;
public WebConfig(AuthInterceptor authInterceptor) {
this.authInterceptor = authInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/public/**");
}
}
2.2 多拦截器执行顺序
当存在多个拦截器时,执行顺序遵循以下规则:
preHandle按配置顺序正序执行postHandle按配置顺序逆序执行afterCompletion按配置顺序逆序执行
可以通过@Order注解或注册顺序显式控制:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor).order(1);
registry.addInterceptor(authInterceptor).order(2);
}
3. 高级应用场景
3.1 动态数据脱敏
结合自定义注解实现灵活的数据脱敏:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataMasking {
MaskType value() default MaskType.NAME;
}
// 拦截器实现
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) {
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
DataMasking annotation = hm.getMethodAnnotation(DataMasking.class);
if (annotation != null && modelAndView != null) {
maskData(modelAndView.getModel(), annotation.value());
}
}
}
3.2 智能限流控制
基于Guava的RateLimiter实现自适应限流:
public class RateLimitInterceptor implements HandlerInterceptor {
private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
String apiKey = request.getHeader("API-Key");
RateLimiter limiter = limiters.computeIfAbsent(apiKey,
k -> RateLimiter.create(getRateLimit(apiKey)));
if (!limiter.tryAcquire()) {
response.setHeader("X-RateLimit-RetryAfter", "1");
return false;
}
return true;
}
}
4. 架构优化实践
4.1 拦截器与过滤器的抉择
虽然功能相似,但两者存在本质差异:
| 特性 | 拦截器(Interceptor) | 过滤器(Filter) |
|---|---|---|
| 作用范围 | Spring MVC上下文 | Servlet容器层面 |
| 依赖关系 | 依赖Spring容器 | 不依赖Spring |
| 执行时机 | Controller方法前后 | Servlet请求处理前后 |
| 异常处理 | 可获取Controller异常 | 无法获取Controller异常 |
| 性能影响 | 较轻量 | 较重(涉及Servlet容器处理) |
4.2 避免循环依赖陷阱
当拦截器与Bean相互依赖时可能引发循环依赖问题。推荐解决方案:
- 懒加载:对依赖Bean使用
@Lazy - 方法注入:通过
ObjectProvider延迟获取 - 静态持有:在
afterPropertiesSet中初始化静态引用
@Component
public class AuditInterceptor implements HandlerInterceptor, InitializingBean {
private static ObjectProvider<AuditService> auditServiceProvider;
private final ObjectProvider<AuditService> provider;
public AuditInterceptor(ObjectProvider<AuditService> provider) {
this.provider = provider;
}
@Override
public void afterPropertiesSet() {
auditServiceProvider = provider;
}
public static AuditService getAuditService() {
return auditServiceProvider.getObject();
}
}
5. 性能调优指南
5.1 耗时监控实现
通过拦截器自动记录方法执行耗时:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
long endTime = System.currentTimeMillis();
long duration = endTime - (long) request.getAttribute("startTime");
metricsService.recordLatency(request.getRequestURI(), duration);
}
5.2 最佳实践建议
- 精简preHandle逻辑:此方法在请求线程同步执行,应保持轻量
- 异步处理:耗时操作应放在
afterCompletion并结合异步线程 - 缓存设计:频繁访问的数据应缓存,避免重复计算
- 异常隔离:每个拦截器应处理自身异常,避免影响其他拦截器
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
try {
return doPreHandle(request);
} catch (Exception e) {
log.error("Prehandle error", e);
return true; // 不影响其他拦截器
}
}
在微服务架构实践中,拦截器常与Feign拦截器、Spring Cloud Gateway过滤器配合使用,形成完整的请求处理链。比如可以在网关层做基础认证,在业务拦截器做细粒度权限控制,最后在Feign拦截器添加服务间调用的认证头,这种分层设计既保证了安全性又不失灵活性。

1214

被折叠的 条评论
为什么被折叠?



