一、类型转换器
1.1、日期类型转换
1、index.jsp添加下列表单
<form action="/my/date" method="post">
年龄:<input type="text" name="age"/><br>
出生日期:<input type="text" name="birthday"/><br>
<button type="submit">登录</button>
</form>
2、MyController处理类
@RequestMapping("/date")
public ModelAndView doDate(int age, Date birthday){
ModelAndView andView = new ModelAndView();
andView.addObject("age",age);
andView.addObject("birthday",birthday);
andView.setViewName("/WEB-INF/jsp/home.jsp");
return andView;
}
3、home.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>年龄:${age}</h1>
<h1>出生日期:${birthday}</h1>
</body>
</html>
4、测试
5、结果
经过多次测试发现:springmvc默认只支持2019/01/22这种字符串格式的日期类型转换。
1.2、自定义类型转换器 —— 自定义日期转换的格式
1、自定义类型转换器
public class MyConverter implements Converter<String, Date> {
@Override
public Date convert(String s) {
Date date = null;
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
date = format.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
注意:ParseException 异常必须处理,不能抛出;自定义的类型转换器必须实现org.springframework.core.convert.converter.Converter接口。
2、spring.xml注册自定义类型转换器
<!--注册自定义类型转换器-->
<bean id="myConverter" class="com.chuan.core.config.MyConverter"/>
<!--注册类型转换器工厂bean-->
<bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="myConverter"/>
</set>
</property>
</bean>
<!--配置类型转换器工厂bean注解扫描-->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
3、测试
4、结果
- 自定义日期格式类型yyyy-MM-dd生效,springmvc原来默认的日期解析格式作废。
1.3、配置多个日期格式
- 自定义类型转换器添加如下代码
public class MyConverter implements Converter<String, Date> {
@Override
public Date convert(String s) {
Date date = null;
SimpleDateFormat format = getSimpleDateFormat(s);
try {
date = format.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
private SimpleDateFormat getSimpleDateFormat(String source){
SimpleDateFormat format = new SimpleDateFormat();
if(Pattern.matches("^\\d{4}-\\d{2}-\\d{2}$",source)){
format = new SimpleDateFormat("yyyy-MM-dd");
}else if(Pattern.matches("^\\d{4}.\\d{2}.\\d{2}$",source)){
format = new SimpleDateFormat("yyyy.MM.dd");
}else if(Pattern.matches("^\\d{4}\\d{2}\\d{2}$",source)){
format = new SimpleDateFormat("yyyyMMdd");
}
return format;
}
- 测试结果显示上述符合上述三种日期格式的字符串参数都能正常请求目标视图
二、数据回显
- 应用场景:登录/注册失败后跳转回登录/注册页面并显示错误信息,提高用户体验
2.1、age信息错误时index.jsp页面回显
1、解决方案
- 需求:表单中 /my/date 请求数据格式有误时跳转回index.jsp页面
- 解决方案:1)获取参数错误是的错误Exception;2)使用@ExceptionHandler处理该异常并在其标记方法中回显至index.jsp页面。
2、测试“age”的异常类型
控制台打印的异常:org.springframework.web.method.annotation.MethodArgumentTypeMismatchException,必须要注册自定义类型处理器,后台才会打印显示这个错误。
3、修改MyController
- 参考上一篇博客:springmvc异常处理
- 修改MyController
@RequestMapping("/date")
public ModelAndView doDate(int age, Date birthday){
ModelAndView andView = new ModelAndView();
andView.addObject("age",age);
andView.addObject("birthday",birthday);
andView.setViewName("/WEB-INF/jsp/home.jsp");
return andView;
}
@ExceptionHandler(TypeMismatchException.class)
public ModelAndView initException(){
return new ModelAndView("/index.jsp");
}
加入一个异常处理使页面跳转至index.jsp即可;注意: @ExceptionHandler(TypeMismatchException.class) 只能在 Mycontroller 中使用,如果再上一篇定义的MyExceptionController 中使用会没有效果,具体原因未知。
4、测试成功:age输入错误是会回显至index.jsp页面
2.2、出生日期错误时页面回显
- 解决思路:与2.1思路相同,处理date数据的异常即可
1、捕获出生日期date类型的异常
try {
date = format.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
- 异常信息就是ParseException ;
2、修改MyController:添加下列代码
@ExceptionHandler(ParseException.class)
public ModelAndView parseException(){
return new ModelAndView("/index.jsp");
}
3、测试结果失败:无法跳转到/index.jsp页面
- 原因分析:ParseException 异常是已经在自定义类型转换器MyConverter中处理了并没有抛出,所有**@ExceptionHandler**并不能捕获到该异常,也就无法处理该方法体的业务逻辑。
4、解决方案
- 修改MyConverter的getSimpleDateFormat方法
private SimpleDateFormat getSimpleDateFormat(String source){
SimpleDateFormat format = new SimpleDateFormat();
if(Pattern.matches("^\\d{4}-\\d{2}-\\d{2}$",source)){
format = new SimpleDateFormat("yyyy-MM-dd");
}else if(Pattern.matches("^\\d{4}.\\d{2}.\\d{2}$",source)){
format = new SimpleDateFormat("yyyy.MM.dd");
}else if(Pattern.matches("^\\d{4}\\d{2}\\d{2}$",source)){
format = new SimpleDateFormat("yyyyMMdd");
}
else{
//在上述三个日期格式时都不符合时,抛出TypeMismatchException异常
throw new TypeMismatchException("12a",Date.class);
}
return format;
}
如上:在上述三个日期格式时都不符合时,抛出TypeMismatchException异常;TypeMismatchException的值设置为**(“12a”,Date.class))**,则这个异常必定会被抛出且抛出的是 date数据相关的异常。
5、重新测试:当日期错误时也能回显至index.jsp页面
2.3、错误信息提示
- 需求:当我们成功回显至页面时,我们还需要将具体的错误信息显示到前端页面,提升用户体验
1、修改MyController的initException方法
@ExceptionHandler(TypeMismatchException.class)
public ModelAndView initException(HttpServletRequest request,Exception ex){
ModelAndView view = new ModelAndView();
//1、获取age和birthday 的请求参数
String age = request.getParameter("age");
String birthday = request.getParameter("birthday");
view.addObject("ex",ex);
String exMessage = ex.getMessage();
//2、校验ex.getMessage()是否包含了age和birthday的值,即判断异常属于哪一个异常
if (exMessage.contains(age)){
view.addObject("error_age","年龄格式有误。。。");
}
if (exMessage.contains(birthday)){
view.addObject("error_birthday","出生日期格式有误。。。");
}
view.setViewName("/index.jsp");
return view;
}
2、测试
年龄和日期均出现错误
3、结果
- 如上图:只会显示一个数据的错误
三、注解式实现日期类型转换
3.1、实现一种日期格式
- MyController:直接应用springMVC的注解@initbinder和spring自带的WebDataBinder类和操作
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
binder.registerCustomEditor(Date.class, new CustomDateEditor(sdf, true));
}
3.2、实现多种日期
- 思路:我们不使用springmvc给我们提供的CustomDateEditor,我们注册自定义的Editor属性编辑器来进行多种日期格式的规范,该编辑器需要继承org.springframework.beans.propertyeditors.PropertiesEditor接口。
1、MyPropertiesEditor属性编辑器类
- 先将自定义的类型解析器删除、以免产生冲突,删除以后**@ExceptionHandler**(TypeMismatchException.class)标记的方法体将会失效。
public class MyPropertiesEditor extends PropertiesEditor {
@Override
public void setAsText(String text) throws IllegalArgumentException {
SimpleDateFormat format = getSimpleDateFormat(text);
try {
Date date = format.parse(text);
setValue(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
private SimpleDateFormat getSimpleDateFormat(String source){
SimpleDateFormat format = new SimpleDateFormat();
if(Pattern.matches("^\\d{4}-\\d{2}-\\d{2}$",source)){
format = new SimpleDateFormat("yyyy-MM-dd");
}else if(Pattern.matches("^\\d{4}.\\d{2}.\\d{2}$",source)){
format = new SimpleDateFormat("yyyy.MM.dd");
}else if(Pattern.matches("^\\d{4}\\d{2}\\d{2}$",source)){
format = new SimpleDateFormat("yyyyMMdd");
}
return format;
}
setAsText:将日期格式注册到属性编辑器当中去。
2、修改MyController类中的initBinder()方法
- 即注册自定义的日期属性编辑器
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Date.class,new MyPropertiesEditor());
}
四、springmvc中数据类型转换过程
4.1、原理图
从上图可以看到,Spring在解析请求参数时,会根据请求格式进入到不同的转换流程:
- 如果是非raw请求(即包含参数数组),则交由ModelAttributeMethodProcessor处理,ModelAttributeMethodProcessor再调用Spring Converter SPI对请求参数逐个进行转换。
- 如果是raw请求,则交由RequestResponseBodyMethodProcessor处理,对于JSON格式的请求体,会再调用MappingJackson2HttpMessageConverter,最终通过ObjectMapper完成转换。
4.2、Spring Boot注册自定义Converter
1、实现org.springframework.core.convert.converter.Converter接口生成一个自定义Converter
public class OffsetDateTimeConverter implements Converter<String, OffsetDateTime> {
@Override
public OffsetDateTime convert(String source) {
if (!NumberUtils.isNumber(source)) {
return null;
}
Long milli = NumberUtils.createLong(source);
return OffsetDateTime.ofInstant(Instant.ofEpochMilli(milli), systemDefault());
}
}
2、选择一个标注@Configuration注解的配置类,继承org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter,然后覆盖addFormatters方法,注册自定义Converter。
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new OffsetDateTimeConverter());
}
}