🎉🎉《Spring Boot实战案例合集》目前已更新126个案例,我们将持续不断的更新。文末有电子书目录。
💪💪永久更新承诺
我们郑重承诺,所有订阅合集的粉丝都将享受永久免费的后续更新服务。
💌💌如何获取
订阅我们的合集《点我订阅》,并通过私信联系我们,我们将第一时间将电子书发送给您。
环境:SpringBoot3.4.2
1. 简介
拦截器是什么?
拦截器是 Spring MVC 的一部分,在 Spring 框架内部工作。它允许针对控制器(Controller)专门修改请求和响应。
什么时候应该使用拦截器?
当需要 Spring 特有的逻辑时,可以使用拦截器,例如:
认证与授权:为控制器验证用户凭据和权限
记录执行时间:测量并跟踪控制器处理请求所需的时间
修改模型/视图:在向客户端发送响应之前调整模型或视图
添加通用属性:在所有响应中包含共享数据(例如,用户详细信息)
如何实现拦截器?
下面是一个非常简单的拦截器,紧紧是打印了一句话。
public class DemoInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.err.println("preHandle...") ;return true ;}public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {System.err.println("postHandle...") ;}public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {System.err.println("afterCompletion...") ;}}
接下来,还需要通过如下方式注册拦截器:
public class DemoConfig implements WebMvcConfigurer {public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/api/*") ;}}
当我们请求以/api开头的请求时,将会执行上面拦截器对应的回调方法,如下:
preHandle...api query...postHandle...afterCompletion...
以上是拦截器从定义、注册到最终工作的整个流程。而在工作中基本我们也是这样去使用的。
而在本篇文章中,我们将介绍其它你不知道的高级玩法。
2.1 拦截器包装器
Spring内部还提供了一个 MappedInterceptor,是一个包装类,它通过 URL 模式(URL patterns)来判断自身是否适用于给定的请求。该类最大的好处是与AbstractHandlerMethodMapping关联,会自动从当前容器中查找该类型的Bean。
首先,定义拦截器:
public class AuthInterceptor implements HandlerInterceptor {private final static Logger logger = LoggerFactory.getLogger(AuthInterceptor.class) ;public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {logger.info("权限验证...") ;return true ;}}
接下来,通过MappedInterceptor注册:
MappedInterceptor authInterceptor(("${pack.auth.patterns}") String[] includePatterns) {return new MappedInterceptor(includePatterns, new AuthInterceptor()) ;}
这样我们就完成了拦截器的注册。
2.2 全局拦截器
我们可以通过继承 AbstractHandlerMapping 类并实现其相关方法,来为应用中的所有请求统一添加拦截器,以此实现对请求处理流程的集中控制与扩展。
public class PackHandlerMapping extends RequestMappingHandlerMapping {// 获取容器中所有的拦截器private final List<HandlerInterceptor> interceptors ;public PackHandlerMapping(List<HandlerInterceptor> interceptors) {this.interceptors = interceptors ;}protected void extendInterceptors(List<Object> interceptors) {List<HandlerInterceptor> ret = this.interceptors.stream()// 我们必须要进行过滤,否则拦截器将会执行两次.filter(interceptor -> !(interceptor instanceof MappedInterceptor)).collect(Collectors.toList()) ;interceptors.addAll(ret) ;}public int getOrder() {return Ordered.HIGHEST_PRECEDENCE ;}}
通过重写父类的extendInterceptors方法,这样我们就为所有的请求添加了拦截器。通过此种方式添加的拦截器不会匹配当前的请求uri。
下面,我们定义如下的拦截器:
public class LoggingInterceptor implements HandlerInterceptor {private final static Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class) ;public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {logger.info("记录日志...") ;return true ;}}
这样所有被AbstractHandlerMapping映射器匹配的请求都会执行上面的拦截器。
2.3 异步拦截器
Spring MVC提供了一个AsyncHandlerInterceptor,该异步拦截器新增了一个方法在异步请求处理开始后被调用的回调方法afterConcurrentHandlingStarted。
当处理器启动异步请求时,DispatcherServlet 会直接退出,而不会像处理同步请求那样调用 postHandle 和 afterCompletion 方法。这是因为异步请求的处理结果(例如 ModelAndView)可能尚未就绪,且将由另一个线程并发生成。在此类场景下,会调用 afterConcurrentHandlingStarted 方法,允许拦截器实现类在释放线程给 Servlet 容器之前执行清理线程绑定属性等任务。
当异步处理完成后,请求会被重新分派到容器进行进一步处理。此时,DispatcherServlet 会再次调用 preHandle、postHandle 和 afterCompletion 方法。如下示例:
public class AsyncLogInterceptor implements AsyncWebRequestInterceptor {// 请求一开始会执行一次public void preHandle(WebRequest request) throws Exception {System.err.printf("AsyncWebRequestInterceptor >>> %s, %s, 开始处理%n", System.currentTimeMillis(), Thread.currentThread().getName()) ;}// 当异步请求结束时执行public void postHandle(WebRequest request, ModelMap model) throws Exception {System.err.printf("AsyncWebRequestInterceptor >>> %s, postHandle%n", Thread.currentThread().getName()) ;}// 当异步请求结束时执行public void afterCompletion(WebRequest request, Exception ex) throws Exception {System.err.printf("AsyncWebRequestInterceptor >>> %s afterCompletion%n", Thread.currentThread().getName()) ;}// 异步请求开始时执行public void afterConcurrentHandlingStarted(WebRequest request) {System.err.printf("AsyncWebRequestInterceptor >>> %s, %s, 异步处理%n", System.currentTimeMillis(), Thread.currentThread().getName()) ;}}
注册拦截器
public class WebInterceptorConfig implements WebMvcConfigurer{public void addInterceptors(InterceptorRegistry registry) {registry.addWebRequestInterceptor(new AsyncLogInterceptor()).addPathPatterns("/admin/**") ;}}
定义Controller接口
@GetMapping("/async")public Callable<String> async() {return new Callable<String>() {public String call() throws Exception {System.err.printf("%s, %s - 执行任务%n", System.currentTimeMillis(), Thread.currentThread().getName()) ;TimeUnit.SECONDS.sleep(3) ;return "异步数据" ;}};}
最终输出结果如下:
推荐文章
Spring Boot 通过 6 种方式实现开关功能,最后一种直接封神
技术专家!Spring Boot 运行时动态修改执行SQL,支持JPA,MyBatis,JDBC
太赞了!AOP弃用@Aspect,一个注解让你的代码灵活十倍!
高级开发!性能优化,Spring Boot 不使用AOP也能优雅的记录日志
生产环境修改Spring Boot配置文件不重启也能实时生效
优雅!Spring 基于 Plugin 插件开发(官方推荐)
高级开发!Spring Boot 乐观锁正确处理的3种方案,第三种方案最优雅
告别内存溢出!Spring StreamingResponseBody 三大实战案例,性能提升100%


