一、SpringMVC 提供的类型转换器
ConversionService 组件:负责数据类型的转换以及格式化功能;
ConversionService有非常多的 convert,不同类型的转换和格式化用它自己的 converter;
Spring MVC 上下文中内建了很多转换器,可完成大多数 Java 类型的转换工作。
以 StringToNumberConverterFactory 为例分析:
{GenericConverter$ConvertiblePair@5229} "java.lang.String -> java.lang.Number" -> {GenericConversionService$ConvertersForPair@5230} "java.lang.String -> java.lang.Number : org.springframework.core.convert.support.StringToNumberConverterFactory@2d148610"
查看:StringToNumberConverterFactory源码,在getConverter()方法中设置断点,在执行set方法(性别字段)前会调用该方法。
源码:
final class StringToNumberConverterFactory implements ConverterFactory<String, Number> {
@Override
public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToNumber<T>(targetType);
}
private static final class StringToNumber<T extends Number> implements Converter<String, T> {
private final Class<T> targetType;
public StringToNumber(Class<T> targetType) {
this.targetType = targetType;
}
@Override
public T convert(String source) {
if (source.length() == 0) {
return null;
}
return NumberUtils.parseNumber(source, this.targetType);
}
}
}
二、类型转换器概述
ConversionService 是 Spring 类型转换体系的核心接口。
public interface ConversionService {
boolean canConvert(Class<?> sourceType, Class<?> targetType);
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
<T> T convert(Object source, Class<T> targetType);
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
ConversionService:是一个接口;它里面有 Convert(转换器)进行工作;
Convert 接口:
public interface Converter<S, T> {
/**
* Convert the source of type S to target type T.
* @param source the source object to convert, which must be an instance of S
* @return the converted object, which must be an instance of T
* @throws IllegalArgumentException if the source could not be converted to the desired target type
*/
T convert(S source);
}
可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC 容器中定义一个ConversionService。
Spring 将自动识别出 IOC 容器中的 ConversionService,并在 Bean 属性配置及 Spring MVC 处理方法入参绑定等场合使用它进行数据的转换;
可通过 ConversionServiceFactoryBean 的 converters 属性注册自定义的类型转换器;
例如:
三、Spring 支持的转换器
Spring 定义了 3 种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到 ConversionServiceFactoryBean 中:
(1)Converter<S,T>:将 S 类型对象转为 T 类型对象
(2)ConverterFactory:将相同系列多个 “同质” Converter 封装在一起。如果希望将一种类型的对象转换为另一种类型及其子类的对象(例如将 String 转换为 Number 及 Number 子类(Integer、Long、Double 等)对象)可使用该转换器工厂类
(3)GenericConverter:会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换
四、自定义类型转换器
需求:在页面使用一个表单快速进行员工信息的添加,在输入框内使用规定的格式进行添加;
前端页面:
<form action="${ctx}/quickAdd" method="get">
<!-- 解决问题:
1.数据类型转换
2.数据格式
3.数据校验
自定义类型转换器:
将字符串转换为Employee对象,完成添加功能
BirthDay :<input type="text" name="birthDay"/><br><br>
-->
<!-- 字符串格式:lastName-email-gender-department.id
例如:GG-gg@atguigu.com-0-105
-->
Employee : <input type="text" name="empInfo"/>
<input type="submit" value="Submit"><br><br>
</form>
控制器方法:
/**
* 快速保存
*
* quickAdd?empInfo=empAdmin-admin@qq.com-0-101
*
* @RequestParam("empInfo") Employee employee
* Employee employee = request.getParameter("empInfo");
*
* @param employee
* @return
*/
@RequestMapping(value = "/quickAdd", method = RequestMethod.GET)
public String quickAdd(@RequestParam("empInfo") Employee employee) {
System.out.println("快速保存:employee = " + employee);
return "success";
}
测试:出错了!
提示信息不能把 String 类型转换为 Employee 类型,因为在SpringMVC中没有找到匹配的转换器。
实现:使用自定义类型转换器,通过一个自定义类型的转换器让其工作
步骤:
(1)ConversionService:是一个接口;它里面有 Convert(转换器)进行工作,实现 Converter 接口,写一个自定义的类型转换器;
(2)Converter 是 ConversionService 中的组件;
① 把自定义的 Converter 得放进 ConversionService 中;
② 将 WebDataBinder 中的 ConversionService 设置成我们这个加了自定义类型转换器的 ConversionService;
(3)让SpringMVC用我们自定义的 ConversionService;
自定义转换器:
/**
* 两个泛型:
* Converter<S, T>
*
* S:source
* T:Target
* 把 S 转换为 T
*
*/
public class MyStringToEmployeeConverter implements Converter<String, Employee> {
@Autowired
DepartmentDao departmentDao;
/**
* 自定义转换规则
* @param source
* @return
*/
@Override
public Employee convert(String source) {
System.out.println("页面提交的将要转换的字符串" + source);
Employee employee = new Employee();
if (source.contains("-")) {
String[] split = source.split("-");
employee.setLastName(split[0]);
employee.setEmail(split[1]);
employee.setGender(Integer.parseInt(split[2]));
employee.setDepartment(departmentDao.getDepartment(Integer.parseInt(split[3])));
}
return employee;
}
}
在SpringMVC 中进行配置:
<!-- 告诉 SpringMVC 别用默认的ConversionService,而用自定义的ConversionService,可能有我们自定义的 Converter-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<!--converters 转换器中添加我们自定义的类型转换器-->
<property name="converters">
<set>
<bean class="com.njf.dataBinder.component.MyStringToEmployeeConverter"></bean>
</set>
</property>
</bean>
<!--conversion-service="conversionService 告诉SpringMVC 使用我们自己配置的类型转换组件-->
<mvc:annotation-driven conversion-service="conversionService"/>
(1)细节一:配置 ConversionService
有一个 ConversionServiceFactoryBean类,是一个 FactoryBean类,用于获取 ConversionService 对象,它里面还存有一系列的 Converters:
(2)细节二:把 ConversionService 注册到SpringMVC 上下文中
<mvc:annotation-driven conversion-service="conversionService"/> 会将自定义的ConversionService 注册到 Spring MVC 的上下文中
测试效果,可以把字符串的值解析出来并转换为入参对象。
总结:
1、实现 Converter 接口,写一个自定义的类型转换器;
2、将这个 Converter 配置在ConversionService中;
3、告诉SpringMVC使用自定义这个ConversionService;
4、源码上的ConversionService已经替换了;
五、debug调试
增加新的转换器,之前的转换器是否还好使呢?好使。
查看,框架出厂设置,与目前将我们的自定义类型转换器加入出厂设置中。
当配置了<mvc:annotation-driven />后,会自动加载
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
和org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter