如何实现数据类型转换
1.类型转换:首先表单数据(全部是字符串)通过 WebDataBinder 进行绑定到命令对象,内部通过 Converter SPI 实现。
2.数据验证:使用 JSR-303 验证框架进行验证
3.格式化显示:在表单页面可以通过如下方式展示通过内部通过 Converter SPI 格式化的数据和错误信息
数据类型转换架构
SpringMVC 数据转换包括两部分
一、类型转换器:提供类型转换的实现支持
1、Converter:类转换器,用于转换 S 类型到 T 类型,此接口的实现必须是线程安全的且可以被共享。
package org.springframework.core.convert.converter;
public interface Converter<S,T>{// S 是源类型 T 是目标类型
T convert(S source); // 转换 S 类型的 source 到 T 目标类型的转换方法
}
2、GenericConverter 和 ConditionalGenericConverter:GenericConverter 接口实现能在多种类型之间进行转换,ConditionalGenericConverter 是有条件的在多种类型之间进行转换。
GenericConverter:
package org.springframework.core.convert.converter;
public interface GenericConverter{
Set<ConvertiblePair> getConvertibleTypes();
Object convert(Object source,TypeDescriptor sourceType,TypeDescriptor targetType);
}
getConvertibleTypes:指定了可以转换的目标类型对;
convert:在 sourceType 和 targetType 类型之间进行转换。
COnditionalGenericConverter:
package org.springframework.core.convert.converter;
public interface ConditionalGenericConverter extends GenericConverter{
boolean matches(TypeDescriptor sourceTyoe,TypeDescriptor targetType);
}
matches:用于判断 sourceType 和 targetType 类型之间能否进行类型转换。
3、ConverterFactory:工厂模式的实现,用于选择将一种 S 源类型转换为 R 类型的子类型 T 的转换器的工厂接口。
package org.springframework.core.convert.converter;
public interface COnvertFactory<S,R>{
<T extends R>Converter<S,T> getConverter(Class<T> targetType); // S:源类型;R:目标类型的父类型;T:目标类型,且是 R 类型的子类型;
}
二、类型转换注册器、类型转换服务:提供类型转换器注册支持,运行时类型转换 API 支持。
1、ConverterRegistry:类型转换器注册支持,可以注册/删除相应的类型转换器。
package org.springframework.core.convert.converter;
public interface ConverterRegistry{
void addConverter(Converter<?,?> converter);
void addConverter(Class<?> sourceType,Class<?> targetType,COnverter<?,?>converter);
void addConverter(GenericConverter converter);
void addConverterFactory(ConverterFactory<?,?> converterFactory);
void removeConvertible(Class<?> sourceType,Class<?> targetType);
}
可以注册:Converter 实现,GenericConverter 实现,ConverterFactory 实现。
2、ConversionService:运行时类型转换服务接口,提供运行期类型转换的支持。
package org.springframework.core.convert;
public interface ConversionService{
boolean canConvert(Class<?> sourceType,Class<?> targetType);
boolean canConvert(TypeDescriptor sourceType,TypeDescriptor targetType);
<T> T convert(Object srouce,Class<T> targetType);
Object convert(Object source,TypeDescriptor sourceType,TypeDescriptor targetType);
}
Spring提供了两个默认实现(其都实现了ConverterRegistry、ConversionService接口):
**DefaultConversionService:默认的类型转换服务实现;
DefaultFormattingConversionService:带数据格式化支持的类型转换服务实现,一般使用该服务实现即可。**
内建的类型转换器
一、标量转换器
类名 | 说明 |
StringToBooleanConverter | String —-> Boolean true:true/on/yes/1; false:false/off/no/0 |
ObjectToStringConverter | Object —-> String 调用 toString 方法转换 |
StringToNumberConverterFactory | String —-> Number (如 Integer、Long 等) |
NumberToNumberConverterFactory | Number 子类型(Integer、Long、Double 等)<—> Number 子类型(Integer、Long、Double 等) |
StringToCharacterConverter | String —-> java.lang.Character 取字符串第一个字符 |
NumberToCharacterConverter | java.lang.Character —-> Number子类型(Integer、Long、Double等) |
StringToEnumConverterFactory | String —-> enum 类型 通过 Enum.valueOf 将字符串转换为需要的 enum 类型 |
EnumToStringConverter | enum 类型 —-> String 返回 enum 对象的 name() |
StringToLocaleConverter | String —-> java.util.Local |
PropertiesToStringConverter | java.util.Properties —-> String 默认通过 ISO-8859-1 解码 |
StringToPropertiesConverter | String —-> java.util.Properties 默认使用 ISO-8859-1 编码 |
二、集合、数组相关转换器
类名 | 说明 |
ArrayToCollectionConverter | 任意 S 数组 —-> 任意 T 集合(List、Set) |
ColleactionToArrayConverter | 任意 T 集合(List、Set) —-> 任意 S 数组 |
ArrayToArrayConverter | 任意 S 数组 <–> 任意 T 数组 |
CollectionToCollectionConverter | 任意 T 集合(List、Set) <–> 任意 T 集合(List、Set) 即集合之间的类型转换 |
MapToMapConverter | Map <–> Map 之间的转换 |
ArrayToStringConverter | 任意 S 数组 —-> String 类型 |
StringToArrayConverter | String —-> 数组 默认通过”,”分割,且去除字符串的两边空格(trim) |
ArrayToObjectConverter | 任意 S 数组 —-> 任意 Object 的转换 (如果目标类型和源类型兼容,直接返回源对象;否则返回 S 数组的第一个元素并进行类型转换) |
ObjectToArrayConverter | Object —-> 单元素数组 |
ColleactionToStringConverter | 任意 T 集合(List、Set) —-> String 类型 |
StringToCollectionConverter | String —-> 集合(List、Set) 默认通过”,”分割,且去除字符串的两边空格(trim) |
CollectionToObjectConverter | 任意 T 集合 —-> 任意 Object 的转换 (如果目标类型和源类型兼容,直接返回源对象;否则返回 S 数组的第一个元素并进行类转换) |
ObjectToCollectionConverter | Object —-> 单元素集合 |
自定义转换类
一、StringToDateConverter:自定义 String 类型转 Date 类型的转换类;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter.DEFAULT;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
public class StringToDateConverter implements Converter<String,Date>{
private final SimpleDateFormat DATE_FORMAT_PATTERN = new SimpleDateFormat("yyyy-MM-dd");
private final SimpleDateFormat DATETIME_FORMAT_PATTERN = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public Date convert(String source) {
Date date = null;
if(!StringUtils.hasLength(source)) {
return date; // 原始字符串为空
}
if(source.contains("-")) { // 处理日期类型
try {
date = DATE_FORMAT_PATTERN.parse(source);
}catch(ParseException e) {
throw new IllegalArgumentException(
String.format("类型转换失败,期望的格式是[yyyy-MM-dd],但是原始格式为[%s]", source));
}
}else if(source.contains("-") && source.contains(":") && 19 == source.length()) { // 处理时间类型
try {
date = DATETIME_FORMAT_PATTERN.parse(source);
}catch(ParseException e) {
throw new IllegalArgumentException(
String.format("类型转换失败,期望的格式是[yyyy-MM-dd HH:mm:ss],但是原始格式为[%s]", source));
}
}else {
throw new IllegalArgumentException(
String.format("类型转换失败,期望的格式是[yyyy-MM-dd]或[yyyy-MM-dd HH:mm:ss],但是原始格式为[%s]", source));
}
return date;
}
}
二、StringToPhoneNumberConverter:自定义 String 类型转 PhoneNumberModel 类型的转换类
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
class PhoneNumberModel{
private String areaCode;
private String phoneNumber;
public String getAreaCode() {
return areaCode;
}
public void setAreaCode(String areaCode) {
this.areaCode = areaCode;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
public class StringToPhoneNumberConverter implements Converter<String,PhoneNumberModel>{
private final Pattern pattern = Pattern.compile("^(\\d{3,4})-(\\d(7,8))$");
@Override
public PhoneNumberModel convert(String source) {
if(!StringUtils.hasLength(source)) {
return null; // 原始字符串为空
}
Matcher matcher = pattern.matcher(source);
if(matcher.matches()) {
PhoneNumberModel phoneNumberModel = new PhoneNumberModel();
phoneNumberModel.setAreaCode(matcher.group(1));
phoneNumberModel.setPhoneNumber(matcher.group(2));
return phoneNumberModel;
}else {
throw new IllegalArgumentException(String.format("类型转换失败,需要格式[000-00000000],但格式是[%s]", source));
}
}
}
配置自定义类型转换器
1、注册ConversionService 实现和自定义的类型转换器
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="全限定类名" />
<!-- 例如:
<bean class="xxx.xxx.converters.StringToPhoneNumberConverter"/>
-->
</list>
</property>
</bean>
2、通过 ConfigurableWebBindingInitializer 注册 ConversionService
<bean id="webBindingInitializer" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService">
</bean>
3、注册 ConfigurableWebBindingInitializer 到 RequestMappingHandlerAdapter
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer" ref="webBindingInitializer">
</bean>