Spring MVC-06循序渐进之Converter和Formatter
概述
Spring MVC-05循序渐进之数据绑定和form标签库(上) 和 Spring MVC-05循序渐进之数据绑定和form标签库(下) 实战从0到1 我们已经学习了数据绑定,见识了数据绑定的方便性。
但是Spring的数据绑定并非没有任何限制, 比如Spring总是试图使用more的语言区域将日期输入绑定到java.uti.Date上,假设我们想让Spring使用不同的格式日期,就需要一个Converter或者Formatter来协助完成了。
本篇博文将重点讨论Converter和Formatter的内容。 这两者均可以用于将一种对象类型转换成另外一种对象类型。 Converter是通用元件,可以在应用程序的任意层使用,而Formatter则是专门为Web层设计的
converter
Spring 的Converter是可以将一种类型转换成另外一种类型的一个对象。
举个例子,用户输入的日期格式可能有许多种,比如“January 10,2018”、“10/01/2018”、“2018-01-10”,这些都表示同一个日期。 默认情况下,Spring会将期待用户输入的日期样式和当前语言区域的日期样式相同。
比如US用户,就是月/日/年的格式。 如果希望Spring在将输入的日期字符串绑定到Date时使用不同的日期格式,则需要编写一个Converter,才能将字符串转换成日期。
Step 1 实现Converter接口
为了创建自定义的Converter需要实现 org.springframework.core.convert.converter.Converter接口
我们来看下该接口
package org.springframework.core.convert.converter;
/**
* A converter converts a source object of type {@code S} to a target of type {@code T}.
*
* <p>Implementations of this interface are thread-safe and can be shared.
*
* <p>Implementations may additionally implement {@link ConditionalConverter}.
*
* @author Keith Donald
* @since 3.0
* @param <S> the source type
* @param <T> the target type
*/
public interface Converter<S, T> {
/**
* Convert the source object of type {@code S} to target type {@code T}.
* @param source the source object to convert, which must be an instance of {@code S} (never {@code null})
* @return the converted object, which must be an instance of {@code T} (potentially {@code null})
* @throws IllegalArgumentException if the source cannot be converted to the desired target type
*/
T convert(S source);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
这里的泛型 S表示源类型, 泛型T表示目标类型。 比如为了创建一个可以将String转为Date的Converter,可以像下面这样声明
public class MyConverter implements Converter<String, Date> {
}
- 1
- 2
- 3
当然了要重写convert方法。
@Override
public Date convert(String source){
}
- 1
- 2
- 3
- 4
该例中的代码如下
MyConverter.java
package com.artisan.converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.core.convert.converter.Converter;
/**
*
* @ClassName: MyConverter
* @Description: 自定义Converter,将String类型的数据转换为指定格式的Date
* @author Mr.Yang
* @date 2018年2月10日
*
*/
public class MyConverter implements Converter<String, Date> {
private String datePattern;
private Date targetFormateDate;
/**
*
* 创建一个新的实例 MyConverter. 默认构造函数
*
*/
public MyConverter() {
super();
}
/**
*
* 创建一个新的实例 MyConverter. 实例化时指定日期格式
*
* @param datePattern
*/
public MyConverter(String datePattern) {
super();
this.datePattern = datePattern;
}
/**
* 重写convert方法
*/
@Override
public Date convert(String source) {
try {
SimpleDateFormat sdf = new SimpleDateFormat(datePattern);
// 是否严格解析日期,设置false禁止SimpleDateFormat的自动计算功能
sdf.setLenient(false);
targetFormateDate = sdf.parse(source);
} catch (ParseException e) {
// the error message will be displayed when using <form:errors>
e.printStackTrace();
throw new IllegalArgumentException("invalid date format. Please use this pattern\"" + datePattern + "\"");
}
return targetFormateDate;
}
}
/**
* 如果设置为true,假设你输入的日期不合法,它会先进行一定的计算.计算出能有合法的值,就以计算后的值为真正的值.
*
* 比如说当你使用的时候有2012-02-31,2012-14-03这样数据去format,
* 如果setLenient(true).那么它就会自动解析为2012-03-02和2013-02-03这样的日期.
* 如果setLenient(false),2012-14-03就会出现解析异常,因为去掉了计算,而这样的数据又是不合法的
*
*/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
Step 2 SpringMVC配置文件中配置bean及设置conversion-service属性
为了在Spring MVC中使用自定义的Converter,需要在SpringMVC的配置文件中配置一个conversionService ,该Bean的名字必须为
org.springframework.context.support.ConversionServiceFactoryBean 。同时必须包含一个converters属性,它将列出要在应用程序中使用的所有定制的Converter.
紧接着要给annotation-driven元素的conversion-service属性赋bean名称。
完整配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描控制层的注解,使其成为Spring管理的Bean -->
<context:component-scan base-package="com.artisan.controller"/>
<!-- 静态资源文件 -->
<!-- (2)将自定义的convert设置给conversion-service属性 -->
<mvc:annotation-driven conversion-service="conversionService"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/*.jsp" location="/"/>
<!-- 视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- (1)自定义converter -->
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.artisan.converter.MyConverter">
<constructor-arg type="java.lang.String" value="MM-dd-yyyy"/>
</bean>
</list>
</property>
</bean>
</beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
小Demo
Domain类
package com.artisan.domain;
import java.io.Serializable;
import java.util.Date;
public class Artisan implements Serializable {
private static final long serialVersionUID = -908L;
private long id;
private String firstName;
private String lastName;
private Date birthDate;
private int salaryLevel;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public int getSalaryLevel() {
return salaryLevel;
}
public void setSalaryLevel(int salaryLevel) {
this.salaryLevel = salaryLevel;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
Controller
package com.artisan.controller;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import com.artisan.domain.Artisan;
/**
*
* @ClassName: ArtisanController
* @Description: @Controller标注的Artisan控制层
* @author Mr.Yang
* @date 2018年2月10日
*
*/
@Controller
public class ArtisanController {
private Logger logger = Logger.getLogger(ArtisanController.class);
@RequestMapping(value="/artisan_input")
public String inputArtisan(Model model) {
model.addAttribute(new Artisan());
return "ArtisanForm";
}
@RequestMapping(value="/artisan_save")
public String saveArtisan(@ModelAttribute Artisan artisan, BindingResult bindingResult,
Model model) {
// 如果输入错误,跳转到ArtisanForm页面
if (bindingResult.hasErrors()) {
FieldError fieldError = bindingResult.getFieldError();
logger.info("Code:" + fieldError.getCode()
+ ", field:" + fieldError.getField());
return "ArtisanForm";
}
// save Artisan here
model.addAttribute("artisan", artisan);
return "ArtisanDetails";
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
前台JSP
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML>
<html>
<head>
<title>Add Artisan Form</title>
<style type="text/css">@import url("<c:url value="/css/main.css"/>");</style>
</head>
<body>
<div id="global">
<form:form commandName="artisan" action="artisan_save" method="post">
<fieldset>
<legend>Add an Artisan</legend>
<p>
<label for="firstName">First Name: </label>
<form:input path="firstName" tabindex="1"/>
</p>
<p>
<label for="lastName">Last Name: </label>
<form:input path="lastName" tabindex="2"/>
</p>
<p>
<!-- 接收显示错误信息 -->
<form:errors path="birthDate" cssClass="error"/>
</p>
<p>
<label for="birthDate">Date Of Birth: </label>
<form:input path="birthDate" tabindex="3" />
</p>
<p id="buttons">
<input id="reset" type="reset" tabindex="4">
<input id="submit" type="submit" tabindex="5"
value="Add Artisan">
</p>
</fieldset>
</form:form>
</div>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
展示页面
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML>
<html>
<head>
<title>Save Artisan</title>
<style type="text/css">@import url("<c:url value="/css/main.css"/>");</style>
</head>
<body>
<div id="global">
<h4>The Artisan details have been saved.</h4>
<p>
<h5>Details:</h5>
First Name: ${artisan.firstName}<br/>
Last Name: ${artisan.lastName}<br/>
Date of Birth: ${artisan.birthDate}
</p>
</div>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
formatter
Formatter就像Converter一样,也是将一种类型转换为另外一种类型,但是Formatter的源类型必须是String,而Converter的源类型可以是任意类型。
Formatter更加适合Web层,而Converter则可以在任意层中。
为了转换SpringMVC应用程序中的表单的用户输入,始终应该选择Formatter而不是Converter
Step 1 实现Formatter接口
我们先看下
org.springframework.format.Formatter的源码
public interface Formatter<T> extends Printer<T>, Parser<T> {
}
- 1
- 2
- 3
public interface Printer<T> {
/**
* Print the object of type T for display.
* @param object the instance to print
* @param locale the current user locale
* @return the printed text string
*/
String print(T object, Locale locale);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
public interface Parser<T> {
/**
* Parse a text String to produce a T.
* @param text the text string
* @param locale the current user locale
* @return an instance of T
* @throws ParseException when a parse exception occurs in a java.text parsing library
* @throws IllegalArgumentException when a parse exception occurs
*/
T parse(String text, Locale locale) throws ParseException;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
可以知道需要重写两个方法
parse方法利用指定Locale将一个String解析为目标类型
print方法与之相反,它是返回目标对象的字符串表示法
来看下具体的用法
编写自定义Formatter类
package com.artisan.converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.springframework.format.Formatter;
public class MyFormatter implements Formatter<Date> {
private String datePattern;
private SimpleDateFormat dateFormat;
public MyFormatter(String datePattern) {
this.datePattern = datePattern;
dateFormat = new SimpleDateFormat(datePattern);
dateFormat.setLenient(false);
}
@Override
public String print(Date date, Locale locale) {
return dateFormat.format(date);
}
@Override
public Date parse(String s, Locale locale) throws ParseException {
try {
return dateFormat.parse(s);
} catch (ParseException e) {
e.printStackTrace();
// the error message will be displayed when using <form:errors>
throw new IllegalArgumentException("invalid date format. Please use this pattern\"" + datePattern + "\"");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
Step 2 SpringMVC配置文件中配置bean及设置conversion-service属性
注册配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描控制层的注解,使其成为Spring管理的Bean -->
<context:component-scan base-package="com.artisan.controller"/>
<!-- 静态资源文件 -->
<!-- (2)将自定义的convert设置给conversion-service属性 -->
<mvc:annotation-driven conversion-service="conversionService"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/*.jsp" location="/"/>
<!-- 视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- (1)自定义fromatter -->
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="com.artisan.converter.MyFormatter">
<constructor-arg type="java.lang.String" value="MM-dd-yyyy" />
</bean>
</set>
</property>
</bean>
</beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
为了在Spring中使用Formatter,需要注册org.springframework.format.support.FormattingConversionServiceFactoryBean这个bean。
这个bean与converter的
org.springframework.context.support.ConversionServiceFactoryBean 不同,FormattingConversionServiceFactoryBean可以用一个formatters属性注册Formatter,也可以用converters属性注册converter
测试结果同上。
用registrar注册formatter
注册Formatter的另外一种方式是使用Registrar.
Step 1 编写MyFormatterRegistrar ,注册自定义Formatter
package com.artisan.converter;
import org.springframework.format.FormatterRegistrar;
import org.springframework.format.FormatterRegistry;
public class MyFormatterRegistrar implements FormatterRegistrar {
private String dataPattern;
public MyFormatterRegistrar(String dataPattern) {
super();
this.dataPattern = dataPattern;
}
@Override
public void registerFormatters(FormatterRegistry registry) {
registry.addFormatter(new MyFormatter(dataPattern));
// u can registry more formatters here
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
Step 2 编写自定义Formatter
package com.artisan.converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.springframework.format.Formatter;
public class MyFormatter implements Formatter<Date> {
private String datePattern;
private SimpleDateFormat dateFormat;
public MyFormatter(String datePattern) {
this.datePattern = datePattern;
dateFormat = new SimpleDateFormat(datePattern);
dateFormat.setLenient(false);
}
@Override
public String print(Date date, Locale locale) {
return dateFormat.format(date);
}
@Override
public Date parse(String s, Locale locale) throws ParseException {
try {
return dateFormat.parse(s);
} catch (ParseException e) {
e.printStackTrace();
// the error message will be displayed when using <form:errors>
throw new IllegalArgumentException("invalid date format. Please use this pattern\"" + datePattern + "\"");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
修改SpringMVC配置文件
有了Registrar,就不用再SpringMVC配置文件中注册任何Formatter了,只要在SpringMVC配置文件中注册Registrar就可以了
Step 3 SpringMVC配置文件中配置bean及设置conversion-service属性
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描控制层的注解,使其成为Spring管理的Bean -->
<context:component-scan base-package="com.artisan.controller"/>
<!-- 静态资源文件 -->
<!-- (2)将自定义的convert设置给conversion-service属性 -->
<mvc:annotation-driven conversion-service="conversionService"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/*.jsp" location="/"/>
<!-- 视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- (1)自定义fromatter -->
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatterRegistrars">
<set>
<bean class="com.artisan.converter.MyFormatterRegistrar">
<constructor-arg type="java.lang.String" value="MM-dd-yyyy" />
</bean>
</set>
</property>
</bean>
</beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
测试结果同上。
converter or formatter 小结
Converter 是一般工具 ,可以将将一种类型转换为另外一种类型,比如将String 转为Date , Long 转为Date .既可以适应在Web层,也可以使用在其他层中。
Formatter的源类型必须是String,比如将String 转为Date , 但是不能将Long 转为Date
**为了转换SpringMVC应用程序中的表单的用户输入,始终应该选择Formatter而不是Converter**Formatter更加使用Web层。
源码
代码已提交到github
https://github.com/yangshangwei/SpringMvcTutorialArtisan
文章来源: artisan.blog.csdn.net,作者:小小工匠,版权归原作者所有,如需转载,请联系作者。
原文链接:artisan.blog.csdn.net/article/details/79201197
- 点赞
- 收藏
- 关注作者
评论(0)