转换、过滤、拦截、异常
1. 转换器
1.1 场景
SpringMVC 默认已经提供了一些常用的类型转换器,例如客户端提交的参数总是字符串类型,SpringMVC 可以将数字自动转换成 int 类型。
然而并不提所有的数据类型都提供了转换器,没有提供的就需要自定义了。例如,不同风格的日期类型的数据就需要自定义转换器。
业务方法
@RequestMapping("/quick11") @ResponseBody public void save11(Date date) { System.out.println(date); }
默认类型转换器
- 地址栏: http://localhost/quick11?date=2021/02/17
- 输出结果: Wed Feb 17 00:00:00 CST 2021
这种格式的字符串无法转换
- 地址栏:http://localhost/quick11?date=2021-02-17
- 结果:HTTP 状态 400 - 错误的请求
1.2 自定义类型转换器
自定义类型转换器的开发步骤:
- 定义转换器类,实现
Converter
接口; - 在
spring-mvc.xml
配置文件中声明转换器; - 在
<annotation-driven>
中引用转换器。
示例:
DataConverter.java
package com.itheima.converter; import org.springframework.core.convert.converter.Converter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateConverter implements Converter<String, Date> { @Override public Date convert(String dateStr) { // 将日期字符串转换成真正的日期对象并返回 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date date = null; try { date = format.parse(dateStr); } catch (ParseException e) { e.printStackTrace(); } return date; } }
spring-mvc.xml
<!-- 配置 mvc 的注解驱动 --> <mvc:annotation-driven conversion-service="conversionService"/> <!-- 声明转换器 --> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <list> <bean class="com.itheima.converter.DateConverter"/> </list> </property> </bean>
结果:
- 解析成功:
- 地址栏:http://localhost/quick11?date=2021-02-17
- 结果:Wed Feb 17 00:00:00 CST 2021
- 原来的反而不能再解析了:
- 地址栏:http://localhost/quick11?date=2021/02/17
- 结果: 控制台报错……
- 解析成功:
2. 全局乱码过滤器
Tomcat 在面对 post 请求时,中文数据会出现乱码,如下的一张表单:
姓名 | 年龄 |
---|---|
张三 | 18 |
李四 | 17 |
在提交后数据显示为
VO{userList=[User{username='??????', age=18}, User={username='??????', age=17}]}
在 SpringMVC 框架下解决乱码问题,可以设置一个过滤器来进行编码的过滤:
web.xml
<!-- 配置全局过滤的 filter 来校正中文乱码 --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
结果示例
姓名 年龄 张三 18 李四 17 VO{userList=[User{username='??????', age=18}, User={username='??????', age=17}]}
3. 拦截器
3.1 简介
拦截器(interceptor)的作用:SpringMVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。
将拦截器按一定的顺序联结成一条链,即成为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
拦截器也是 AOP 思想的具体实现。
拦截器与过滤器的区别:
区别 | 过滤器 | 拦截器 |
---|---|---|
使用范围 | 是 Servlet 规范中的一部分,任何 Java Web 工程都可以使用 | 是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用 |
拦截范围 | 在 url-pattern 中配置了/*之后,可以对所有要访问的资源拦截 | 只会拦截访问的控制器方法,如果访问的是 jsp、html、css、image、js,是不会进行拦截的 |
3.2 使用拦截器
当拥有多个拦截器时,其按照在 spring-mvc.xml 配置文件中出现的顺序先后执行,参照过滤器
Filter
。
创建拦截器类,实现 HandlerInterceptor 接口:
package com.itheima.interceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInterceptor implements HandlerInterceptor { /** * 在目标方法执行之前执行 * * @return // 返回 true 代表放行,返回 false 代表拦截 * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle......."); return false; } // 在目标方法执行之后、视图对象返回之前执行 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle......"); } // 在流程都执行完毕后执行 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion......"); } }
配置拦截器:spring-mvc.xml
<!-- 配置拦截器 --> <mvc:interceptors> <mvc:interceptor> <!-- 对哪些资源执行拦截操作,这里设置全部 --> <mvc:mapping path="/*"/> <bean class="com.itheima.interceptor.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
测试
令 preHandle 返回 false 时:
浏览器 控制台输出 localhost/target
web 页面显示:preHandle....... 令 preHandle 返回 true 时:
浏览器 控制台输出 localhost/target
web 页面显示:Hello World!itcast
preHandle.......
目标资源执行......
postHandle......
afterCompletion......
3.3 拦截器方法说明
方法 | 说明 |
---|---|
preHandle | 此方法在请求处理之前进行调用,其返回值为 Boolean 类型。当其返回 false,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当返回 true,则会调用下一个 Interceptor 的 preHandle() 方法。 |
postHandle | 此方法在当前请求进行处理之后、DispatcherServlet 进行视图返回渲染之前被调用,前提是 preHandle() 方法的返回值为 true。此方法可用于对 Controller 处理之后的 ModelAndView 对象进行操作。 |
afterCompletion | 此方法在整个请求结束之后(也就是在 DispatcherServlet 渲染了对应的视图之后)执行,前提也是 preHandle() 方法的返回值为 true。 |
4. 静态资源访问
SpringMVC 开放静态资源的访问有两种方式:
方案一:在 spring-mvc.xml 中使用
<mvc:resources mapping="/js/** location="/js/" />
注解表明开放哪些资源的访问。<mvc:resources mapping="/js/** location="/js/" /> <mvc:resources mapping="/img/** location="/img/" />
mapping 表明映射地址,location 表明真实路径。
方案二:直接在 spring-mvc.xml 中使用
<mvc:default-servlet-handler/>
注解,其含义是当 SpringMVC 框架找不到对应的资源时,将权限交给原始的容器(即 Tomcat)去找该资源。
5. 异常处理机制
5.1 前言
参见视频 https://www.bilibili.com/video/BV1WZ4y1H7du?p=125 。
系统的 Dao、Service、Controller 出现异常时,都通过 throws Exception 向上抛出,最后由 SpringMVC 前端控制器交由异常处理器进行异常处理,如下图:
SpringMVC 异常处理的两种方式:
使用 SpringMVC 提供的简单异常处理器
SimpleMappingExceptionResolver
;自定义自己的异常处理器,需实现 Spring 的异常处理接口
HandlerExceptionResolver
。
5.2 简单异常处理器SimpleMappingExceptionResolver
在使用时可以根据项目情况进行相应异常类型与视图的映射配置。
<!-- 配置简单映射异常处理器 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 默认错误视图 -->
<property name="fefaultErrorView" value="error" />
<property name="exceptionMappings">
<map>
<!-- 异常类型:com.itheima.exception.MyException
错误视图:error
-->
<entry key="com.itheima.exception.MyException" value="error" />
<entry key="java.lang.ClassCastException" value="error" />
</map>
</property>
</bean>
5.3 自定义异常处理
创建异常处理器类实现
HandlerExceptionResolver
;package com.itheima.resolver; import com.itheima.exception.MyException; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet. ; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyExceptionResolver implements HandlerExceptionResolver { /** * @param e:异常对象 * @return ModelAndView:要跳转到的错误视图的信息 */ @Override public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { ModelAndView modelAndView = new ModelAndView(); if (e instanceof MyException) { modelAndView.addObject("info", "自定义异常"); } else if (e instanceof ClassCastException) { modelAndView.addObject("info", "类型转换异常"); } modelAndView.setViewName("error"); return modelAndView; } }
配置异常处理器(只要配置个 bean 就行,因为处理操作已在异常处理器内实现)——spring-mvc.xml
<!-- 自定义异常处理器 --> <bean class="com.itheima.resolver.MyExceptionResolver"/>
编写异常页面——error.jsp;
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Error</title> </head> <body> <h1>通用的错误提示页面</h1> <h1>${info}</h1> </body> </html>
测试异常跳转 。