SpringMVC
拦截器
Spring MVC 也可以使用拦截器对请求进行拦截处理, 可以自定义拦截器来实现特定的功能, 自定义的拦截器可以实现 HandlerInterceptor 接口中的三个方法, 也可以继承 HandlerInterceptorAdapter 适配器类按照需要那个方法, 就实现哪个方法
过滤器与拦截器区别
过滤器: 过滤器在 Servlet 之前操作
拦截器: 拦截器在 Servlet 之后, 请求处理器 (Controller) 之前操作.
拦截器三个方法
1 preHandle(): 这个方法在 (Controller) 处理器处理请求之前被调用, 在该方法中对用户请求 request 进行处理. 如果该拦截器对请求进行拦截处理后还要调用其他的拦截器, 或者是业务处理器去进行处理, 则返回 true; 如果不需要再调用其他的组件去处理请求, 则返回 false.(如果返回 false 则后续操作都不再执行, 类似于过滤器的 doFilter 所以正常情况下不要返回 false)
2 postHandle(): 这个方法在 (Controller) 处理器处理完请求后, 但是 DispatcherServlet 向客户端返回响应前 (在视图渲染之前) 被调用, 在该方法中对用户请求 request 进行处理.
3 afterCompletion(): 这个方法在 DispatcherServlet 完全处理完请求后 (转发 | 重定向 之后) 被调用, 可以在该方法中进行一些资源清理关闭的操作.
配置拦截器
- <mvc:interceptors>
- <!-- 为所有请求设置拦截器 也可用 ref 引用已经装配好的拦截器 -->
- <bean id="firstHandlerInterceptor" class="main.controller.FirstHandlerInterceptor"></bean>
- <mvc:interceptor>
- <!-- 表示指定拦截器只拦截 / test / 下的所有请求 -->
- <mvc:mapping path="/test/**/"/>
- <!-- 表示访问 / test/test.do 的请求不会触发拦截器 -->
- <mvc:exclude-mapping path="/test/test.do"/>
- <!-- 为指定的请求设置拦截器 也可用 ref 引用已经装配好的拦截器 -->
- <bean id="testInterceptor" class="main.controller.TestInterceptor"></bean>
- </mvc:interceptor>
- </mvc:interceptors>
程序执行顺序
1. preHandle(): 执行请求处理器的请求 (Controller) 方法之前执行.
2. 执行请求处理器的请求 (Controller) 方法
3. postHandle(): 执行请求处理器的请求 (Controller) 方法之后, 在视图渲染之前.
4. 视图渲染
5. afterCompletion(): 视图渲染 (转发 | 重定向) 之后执行.
多个拦截器的执行流程
当存在多个拦截器时的执行顺序, 由配置的先后顺序决定.(preHandle() 先配置, 先执行)
preHandle(): 与拦截器配置的先后顺序一致.
postHandle(): 与拦截器配置的先后顺序相反. 底层倒序循环调用的
afterCompletion(): 与拦截器配置的先后顺序相反.
preHandle()返回值为 false 时的工作原理
第一个拦截器的 preHandle()的返回值为 false:
只执行第一个拦截器的 prehandle()方法, 执行完, return;(后续的方法都不执行)
不是第一个拦截器的 preHandle()的返回值为 false:
当前拦截器之前的拦截器的 afterCompletion()都会被执行.
当两个拦截器的 preHandle() 方法都返回 true 时, 按照虚线路执行
当第二个拦截器 preHandle() 方法返回 false 时按照实现路线执行
异常处理
在 SpringMVC 中, 无论请求控制器中是否存在异常, 都会返回 ModelAndView 对象
Spring MVC 通过 HandlerExceptionResolver 处理程序的异常, 包括 Handler 映射, 数据绑定以及目标方法执行时发生的异常
DispatcherServlet 默认装配的 HandlerExceptionResolver 有 DefaultHandlerExceptionResolver 解析器会自动将标准的 Spring MVC 异常解析为 HTTP 错误状态码
使用 < mvc:annotation-driven/> 配置会装配 Spring3.0 后新增的异常解析器, 实现更精细化处理. 如果希望对所有异常进行统一处理或指定某一异常跳转页面, 可以使用 SimpleMappingExceptionResolver, 它将异常类名映射为视图名, 可实现跳转到指定页面, 并报告异常.
配置异常解析器
- <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
- <!-- 为所有异常定义默认的处理页面, exceptionMappings 未定义的,
- value 表示跳转页面, 至于文件路径和后缀已经在 viewResolver 中指定 -->
- <property name="defaultErrorView" value="error"></property>
- <!-- 定义异常处理页面用来获取异常信息的变量名, 被存放到 request 域中 -->
- <property name="exceptionAttribute" value="exception"></property>
- <!-- 需要特殊处理的异常, 全类名作为 key, 异常页文件名作为值, 可将不同的异常映射到不同的页面上 -->
- <property name="exceptionMappings">
- <props>
- <prop key="java.lang.NullPointerException">nullPointer</prop>
- </props>
- </property>
- </bean>
- <mvc:annotation-driven/>
是 spring MVC 为 @Controllers 分发请求所必须的, 即启用注解驱动, 解决了 @Controller 注解使用的前提配置.
同时它还提供了: 数据绑定支持,@NumberFormatannotation 支持,@DateTimeFormat 支持,@Valid 支持, 读写 xml 的支持(JAXB, 读写 JSON 的支持(Jackson).
它会自动为我们注册了很多的 Bean, 最重要的就是 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter.
第一个是 HandlerMapping 的实现类, 它会处理 @RequestMapping 注解, 并将其注册到请求映射表中.
第二个是 HandlerAdapter 的实现类, 它是处理请求的适配器, 说白了, 就是确定调用哪个类的哪个方法, 并且构造方法参数, 返回值.
简单的说, 用什么注解, 就需要声明对应的 BeanPostProcessor. 而 Spring 为我们提供了一种极为方便注册这些 BeanPostProcessor 的方式, 即使用各种标签来隐式地向 Spring 容器注册
Spring 工作流程
相关类
HandlerMapping(请求处理器的映射对象): 定义了一个所有请求和请求处理器对象之间的映射关系对象
HandlerExecutionChain(请求处理器执行链对象): 定义了 当前请求处理器对象, 和所有拦截器对象.
HandlerAdapter(请求处理器的适配器对象): 调用当前请求处理器的请求方法.
执行流程对应下图理解
1)用户向服务器发送请求, 请求被 SpringMVC 前端控制器 DispatcherServlet 捕获
2)DispatcherServlet 对请求 URL 进行解析, 得到请求资源标识符(URI): 判断请求 URI 对应的映射
1 不存在:
再判断是否配置了 mvc:default-servlet-handler:
如果没配置, 则控制台报映射查找不到, 客户端展示 404 错误
如果有配置, 则执行目标资源(一般为静态资源, 如: JS,CSS,html)
2 存在:
执行下面流程
3)根据该 URI, 调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及 Handler 对象对应的拦截器), 最后以 HandlerExecutionChain 对象的形式返回;
4)DispatcherServlet 根据获得的 Handler, 选择一个合适的 HandlerAdapter.
5)如果成功获得 HandlerAdapter 后, 此时将开始执行拦截器的 preHandler(...)方法[正向]
6)提取 Request 中的模型数据, 填充 Handler 入参, 开始执行 Handler(Controller)方法, 处理请求. 在填充 Handler 的入参过程中, 根据你的配置, Spring 将帮你做一些额外的工作:
1 HttpMessageConveter: 将请求消息 (如 JSON,xml 等数据) 转换成一个对象, 将对象转换为指定的响应信息
2 数据转换: 对请求消息进行数据转换. 如 String 转换成 Integer,Double 等
3 数据格式化: 对请求消息进行数据格式化. 如将字符串转换成格式化数字或格式化日期等
4 数据验证: 验证数据的有效性(长度, 格式等), 验证结果存储到 BindingResult 或 Error 中
7)Handler 执行完成后, 向 DispatcherServlet 返回一个 ModelAndView 对象;
8)此时将开始执行拦截器的 postHandle(...)方法[逆向]
9)根据返回的 ModelAndView(此时会判断是否存在异常: 如果存在异常, 则执行 HandlerExceptionResolver 进行异常处理)选择一个适合的 ViewResolver(必须是已经注册到 Spring 容器中的 ViewResolver)返回给 DispatcherServlet, 根据 Model 和 View, 来渲染视图
10)在返回给客户端时需要执行拦截器的 AfterCompletion 方法[逆向]
11)将渲染结果返回给客户端
Spring 与 SpringMVC
spring 容器与 springMVC 容器对象的关系
springMVC 容器对象, 默认交个 DispatcherServlet 管理
spring 容器对象, 需要我们管理(交给 Listener 管理)
spring 容器对象描述
Root WebApplicationContext: root of context hierarchy
springMVC 容器对象描述
WebApplicationContext for namespace 'springDispatcherServlet-servlet':root of context hierarchy
spring 容器对象是父, springMVC 容器对象是子. 子类可以直接调用父类方法.
SpringMVC 的 IoC 容器中的 bean 可以引用 Spring IoC 容器中的 bean. 反之则不行.
在 Web 应用下, 获取 spring 容器对象方式
- ServletContext servletContext = httpSession.getServletContext();
- ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
在 Web 应用下, 获取 springMVC 容器对象可直接通过装配属性的方式获取
- @Autowired
- private XmlWebApplicationContext context;
配置文件
若 Spring 的 IoC 容器和 SpringMVC 的 IoC 容器扫描组件的包有重合的部分, 就会导致有的 bean 会被创建 2 次, 可通过一个包含一个排除的方式解决
来源: https://www.cnblogs.com/Open-ing/p/12230419.html