Java充电社
专辑
博文
联系我
本人继续续收门徒,亲手指导
SpringMVC教程
-> 拦截器怎么用?
1、Helloword
2、@Controller、@RequestMapping
3、接口测试利器
4、如何接受请求中的参数?
5、@RequestBody接收Json格式数据
6、多文件上传
7、返回页面常见的5种方式
8、返回json格式数据 & 通用返回值设计
9、SpringMVC返回null是什么意思?
10、异步处理请求
11、如何集成静态资源?
12、拦截器怎么用?
13、统一异常处理
14、实战篇:通用返回值 & 异常处理设计
15、全注解的方式 & 原理解析
16、源码解析SpringMVC处理请求的流程
17、源码解析SpringMVC容器的启动过程
18、RequestBodyAdvice:对@ReuqestBody进行增强
19、ResponseBodyAdvice:对@ResponseBody进行增强
20、RESTful接口详解
21、接口调用利器RestTemplate
22、参数解析器HandlerMethodArgumentResolver解密
23、@RequestParam用法及原理详解
24、@RequestBody原理解密
25、@RequestHeader详解
26、@CookieValue详解
27、@RequestAttribute详解
28、@SessionAttribute详解
29、重定向和转向详解
30、Converter转换器详解
31、跨域问题详解
32、类容协商,颠覆你的认知
33、终章
34、CORS通信
35、浏览器安全策略 & CORS
36、Http中的Content-Type详解
上一篇:如何集成静态资源?
下一篇:统一异常处理
<div style="display:none"></div> **文末可领取最近刚整理的,后端必备的200本书籍。** ## 1、本文内容 - 回顾下springmvc处理请求的过程(流程图) - 如何干预springmvc的处理流程? - 加入拦截器后springmvc的处理过程 - 拦截器的用法(具体2个步骤) - 多个拦截器的执行顺序 - 通过案例验证拦截器的执行顺序 - 一起读源码 - 领取后端必备的200本书籍 ## 2、回顾下springmvc处理请求的过程 简化下过程,如下图,过程还是非常简单的 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/223/69320ae9-7af1-4d79-b7f7-62724b35bfc7.png) ## 3、如何干预springmvc的处理流程? 比如我们的系统中,除了登录的方法,其他所有方法都需要先验证一下用户是否登录了,若未登录,让用户先跳转到登录页面,最笨的方法是在所有需要验证的方法内部都加上验证的代码,那么有没有更好的方法呢? 如下图,如果我们将验证登录的代码放在`调用自定义controller的方法`之前,是不是就特别爽了,就不用在所有代码中都添加验证代码了: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/223/ae671f5d-c903-4ec6-88dd-6102a8c712e8.png) springmvc确实为我们考虑到了这种需求,springmvc在处理流程中为我们提供了3个扩展点可以对整个处理流程进行干预,这个就是springmvc中拦截器提供的功能,下面咱们来看一下拦截器怎么玩的。 ## 4、拦截器(HandlerInterceptor) springmvc中使用`org.springframework.web.servlet.HandlerInterceptor`接口来表示拦截器,如下,提供了3个默认方法。 ```java public interface HandlerInterceptor { default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } } ``` 下面来解释下这3个方法。 ### preHandle方法 > 在调用自定义的controller之前会调用这个方法,若返回false,将跳过controller方法的调用,否则将进入到controller的方法中。 ### postHandle方法 > 调用自定义controller中的方法之后会调用这个方法,此时还没有渲染视图,也就是还没有将结果输出到客户端 ### afterCompletion方法 > 整个请求处理完毕之后,即结果以及输出到客户端之后,调用这个方法,此时可以做一些清理的工作,注意这个方法最后一个参数是Exception类型的,说明这个方法不管整个过程是否有异常,这个方法都会被调用。 ### 其他说明 - 3个方法中的hander参数表示处理器,通常就是我们自定义的controller ## 5、加入拦截器后springmvc的处理流程 > 加入拦截器之后处理流程如下图,注意黄色背景的几个对应拦截器的3个方法。 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/223/040956c3-83e2-4d78-81db-b07f3e89accb.png) ## 6、拦截器的用法(2步骤) ### step1:定义拦截器 > 自定义一个类,需要实现`org.springframework.web.servlet.HandlerInterceptor`接口,如下,然后实现具体的方法 ```java public class HandlerInterceptor1 implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(this.getClass().getSimpleName() + ".preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println(this.getClass().getSimpleName() + ".postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println(this.getClass().getSimpleName() + ".afterCompletion"); } } ``` ### step2:将自定义的拦截器添加到springmvc配置文件中 > 配置如下,需要将自定义的拦截器添加到springmvc配置文件中 > > - 可以同时配置多个拦截器,每个拦截器通过<mvc:interceptor>标签来定义,多个拦截器之间可以指定顺序,顺序和<mvc:interceptor>定义的顺序一致 > - 每个拦截器需要指定实现类、拦截的url、排除的url ```xml <!-- interceptors用来定义拦截器,其内部可以定义多个拦截器 --> <mvc:interceptors> <!-- mvc:interceptor 标签用来定义一个拦截器 --> <mvc:interceptor> <!-- 用来指定拦截器匹配的url,比如/user/**会拦截所有以/user开头的url --> <mvc:mapping path="/user/**"/> <!-- 用来指定拦截器排除的url,即这些url不会被拦截器拦截 --> <mvc:exclude-mapping path="/user/login"/> <!-- 用来指定拦截器 --> <bean class="com.javacode2018.springmvc.chat09.intercetor.HandlerInterceptor1"/> </mvc:interceptor> <!-- 其他拦截器配置信息 --> <mvc:interceptor> ..... </mvc:interceptor> </mvc:interceptors> ``` ## 7、多个拦截器时如何执行? > 当请求的url匹配到多个拦截器的时候,执行顺序如下图 > > - preHandler方法是顺序执行的,即和定义的顺序是一致的 > - 而拦截器中的其他2个方法postHandler、afterCompletion是逆序执行的,和pewHandler的顺序相反 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/223/7a82933f-4f27-4993-a89e-96c80fd4e78c.png) ## 8、案例验证拦截器的执行顺序 下面通过案例结合3个场景来看一下拦截器的执行顺序,加深大家的理解。 ### 准备代码 #### UserController ```java package com.javacode2018.springmvc.chat09.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @RequestMapping("/login") public String login() { return "login view"; } @RequestMapping("/add") public String add() { return "add view"; } @RequestMapping("/del") public String modify() { return "modify view"; } @RequestMapping("/list") public String list() { return "list view"; } } ``` #### 创建2个拦截器 ##### 拦截器1:HandlerInterceptor1 ```java package com.javacode2018.springmvc.chat09.intercetor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HandlerInterceptor1 implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(this.getClass().getSimpleName() + ".preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println(this.getClass().getSimpleName() + ".postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println(this.getClass().getSimpleName() + ".afterCompletion"); } } ``` ##### 拦截器2:HandlerInterceptor2 ```java package com.javacode2018.springmvc.chat09.intercetor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HandlerInterceptor2 implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(this.getClass().getSimpleName() + ".preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println(this.getClass().getSimpleName() + ".postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println(this.getClass().getSimpleName() + ".afterCompletion"); } } ``` #### 配置文件中配置拦截器 > 下面将2个拦截器加入springmvc的配置文件中,都会拦截/user开头的所有请求,/user/login被排除在外 ```xml <!-- interceptors用来定义拦截器,其内部可以定义多个拦截器 --> <mvc:interceptors> <!-- mvc:interceptor 标签用来定义一个拦截器 --> <mvc:interceptor> <!-- 用来指定拦截器匹配的url --> <mvc:mapping path="/user/**"/> <!-- 用来指定拦截器排除的url,即这些url不会被拦截器拦截 --> <mvc:exclude-mapping path="/user/login"/> <!-- 用来指定拦截器 --> <bean class="com.javacode2018.springmvc.chat09.intercetor.HandlerInterceptor1"/> </mvc:interceptor> <!-- mvc:interceptor 标签用来定义一个拦截器 --> <mvc:interceptor> <!-- 用来指定拦截器匹配的url --> <mvc:mapping path="/user/**"/> <!-- 用来指定拦截器排除的url,即这些url不会被拦截器拦截 --> <mvc:exclude-mapping path="/user/login"/> <!-- 用来指定拦截器 --> <bean class="com.javacode2018.springmvc.chat09.intercetor.HandlerInterceptor2"/> </mvc:interceptor> </mvc:interceptors> ``` ### 场景1 按照下列表格,调整下2个拦截器的preHandle方法返回值 | 拦截器 | preHandle方法返回值 | | ------------------- | ------------------- | | HandlerInterceptor1 | true | | HandlerInterceptor2 | true | 访问`http://localhost:8080/chat09/user/add`,输出 ```html HandlerInterceptor1.preHandle HandlerInterceptor2.preHandle HandlerInterceptor2.postHandle HandlerInterceptor1.postHandle HandlerInterceptor2.afterCompletion HandlerInterceptor1.afterCompletion ``` ### 场景2 按照下列表格,调整下2个拦截器的preHandle方法返回值 | 拦截器 | preHandle方法返回值 | | ------------------- | ------------------- | | HandlerInterceptor1 | false | | HandlerInterceptor2 | true | 访问`http://localhost:8080/chat09/user/add`,输出 ```html HandlerInterceptor1.preHandle ``` ### 场景3 按照下列表格,调整下2个拦截器的preHandle方法返回值 | 拦截器 | preHandle方法返回值 | | ------------------- | ------------------- | | HandlerInterceptor1 | true | | HandlerInterceptor2 | false | 访问`http://localhost:8080/chat09/user/add`,输出 ```html HandlerInterceptor1.preHandle HandlerInterceptor2.preHandle HandlerInterceptor1.afterCompletion ``` ## 9、源码解析 拦截器的执行过程主要位于下面这段代码中 ```java 代码位置:org.springframework.web.servlet.DispatcherServlet#doDispatch ``` 如下代码,咱们将关键代码提取出来,大家注意看注释,解释了每个方法内部干的事情,具体每个方法的内部,咱们就不进去了,很简单,有兴趣的可以自己去看,这里给大家提点建议:看源码的时候,先站在高的层次上面看代码,了解大的功能及流程之后,再去细看某个功能点,要避免上来就陷入细节中,容易迷失方向。 ```java protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; try { Exception dispatchException = null; try { //①、根据请求找到处理器 mappedHandler = getHandler(processedRequest); HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); //②、内部会调用拦截器的preHandler方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } //③、调用实际的处理器(即这里面会调用咱们controller中的方法) ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //④、调用拦截器的postHandle方法 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } //⑤、渲染视图 & 调用拦截器的afterCompletion processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { //⑥:异常情况,调用拦截器的afterCompletion triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } } ``` ## 10、案例代码 ```html git地址:https://gitee.com/javacode2018/springmvc-series ``` ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/223/a5705c7e-6841-45ee-8bb1-ba47ae579dcc.png) <a style="display:none" target="_blank" href="https://mp.weixin.qq.com/s/_S1DD2JADnXvpexxaBwLLg" style="color:red; font-size:20px; font-weight:bold">继续收门徒,亲手带,月薪 4W 以下的可以来找我</a> ## 最新资料 1. <a href="https://mp.weixin.qq.com/s?__biz=MzkzOTI3Nzc0Mg==&mid=2247484964&idx=2&sn=c81bce2f26015ee0f9632ddc6c67df03&scene=21#wechat_redirect" target="_blank">尚硅谷 Java 学科全套教程(总 207.77GB)</a> 2. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484192&idx=1&sn=505f2faaa4cc911f553850667749bcbb&scene=21#wechat_redirect" target="_blank">2021 最新版 Java 微服务学习线路图 + 视频</a> 3. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484573&idx=1&sn=7f3d83892186c16c57bc0b99f03f1ffd&scene=21#wechat_redirect" target="_blank">阿里技术大佬整理的《Spring 学习笔记.pdf》</a> 4. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484544&idx=2&sn=c1dfe907cfaa5b9ae8e66fc247ccbe84&scene=21#wechat_redirect" target="_blank">阿里大佬的《MySQL 学习笔记高清.pdf》</a> 5. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485167&idx=1&sn=48d75c8e93e748235a3547f34921dfb7&scene=21#wechat_redirect" target="_blank">2021 版 java 高并发常见面试题汇总.pdf</a> 6. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485664&idx=1&sn=435f9f515a8f881642820d7790ad20ce&scene=21#wechat_redirect" target="_blank">Idea 快捷键大全.pdf</a> ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/1/2883e86e-3eff-404a-8943-0066e5e2b454.png)
#custom-toc-container