一、数据绑定流程
- Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象
- DataBinder 调用装配在 Spring MVC 上下文中的 ConversionService 组件进行数据类型转换、数据格式化工作。将 Servlet 中的请求信息填充到入参对象中
- 调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果 BindingData 对象
- Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参
Spring MVC 通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中。数据绑定的核心部件是 DataBinder,运行机制如下:
Spring MVC 上下文中内建了很多转换器,可完成大多数 Java 类型的转换工作
ConversionService converters =
java.lang.Boolean -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@f874ca
java.lang.Character -> java.lang.Number : CharacterToNumberFactory@f004c9
java.lang.Character -> java.lang.String : ObjectToStringConverter@68a961
java.lang.Enum -> java.lang.String : EnumToStringConverter@12f060a
java.lang.Number -> java.lang.Character : NumberToCharacterConverter@1482ac5
java.lang.Number -> java.lang.Number : NumberToNumberConverterFactory@126c6f
java.lang.Number -> java.lang.String : ObjectToStringConverter@14888e8
java.lang.String -> java.lang.Boolean : StringToBooleanConverter@1ca6626
java.lang.String -> java.lang.Character : StringToCharacterConverter@1143800
java.lang.String -> java.lang.Enum : StringToEnumConverterFactory@1bba86e
java.lang.String -> java.lang.Number : StringToNumberConverterFactory@18d2c12
java.lang.String -> java.util.Locale : StringToLocaleConverter@3598e1
java.lang.String -> java.util.Properties : StringToPropertiesConverter@c90828
java.lang.String -> java.util.UUID : StringToUUIDConverter@a42f23
java.util.Locale -> java.lang.String : ObjectToStringConverter@c7e20a
java.util.Properties -> java.lang.String : PropertiesToStringConverter@367a7f
java.util.UUID -> java.lang.String : ObjectToStringConverter@112b07f ……
二、自定义类型转换器
- ConversionService 是 Spring 类型转换体系的核心接口。
- 可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC 容器中定义一个 ConversionService. Spring 将自动识别出 IOC 容器中的 ConversionService,并在 Bean 属性配置及 Spring MVC 处理方法入参绑定等场合使用它进行数据的转换
- 可通过 ConversionServiceFactoryBean 的 converters 属性注册自定义的类型转换器
三、Spring 支持的转换器
Spring 定义了 3 种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到 ConversionServiceFactroyBean 中:
- Converter<S,T>:将 S 类型对象转为 T 类型对象
ConverterFactory:将相同系列多个 “同质” Converter 封装在一起。如果希望将一种类型的对象转换为另一种类型及其子类的对象(例如将 String 转换为 Number 及 Number 子类(Integer、Long、Double 等)对象)可使用该转换器工厂类GenericConverter:会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换
四、自定义转换器示例
<mvc:annotation-driven conversion-service=“conversionService”/> 会将自定义的 ConversionService 注册到 Spring MVC 的上下文中
五、代码演示
总结三步:
1),实现转换器接口,做一个自定义类型的转换器
2),将这个转换器配置在ConversionService中
3),告诉SpringNMVC使用这个ConversionService
4),源码上WebDataBinder上的ConversionService组件就替换了;
1. bean 类
//张三-123456-18
public class User {
private String userName;
private String password;
private Integer age;
2. 编写转换器
/**
* 一个全局User类型转化器
*/
public class UserConverter implements Converter<String, User> {
@Override
public User convert(String strUser) {
// 张三-123456-18
if (strUser != null) {
String[] userArr = strUser.split("-");
User user = new User();
user.setUserName(userArr[0]);
user.setPassword(userArr[1]);
user.setAge(Integer.parseInt(userArr[2]));
//完成类型转换,返回user对象
return user;
}
return null;
}
}
//
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String date) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
try {
return dateFormat.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
3. 配置转换器
<!-- 1. 告诉SpringMVC别用默认的ConversionService,
而用我自定义的ConversionService、可能有我们自定义的Converter; -->
<!-- 以后写自定义类型转换器的时候,就使用FormattingConversionServiceFactoryBean来注册;
既具有类型转换还有格式化功能 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!--converters转换器中添加我们自定义的类型转换器 -->
<property name="converters">
<set>
<!-- String - > User -->
<bean class="com.bug.component.UserConverter"/>
<!-- String - > Date -->
<bean class="com.bug.component.DateConverter"/>
</set>
</property>
</bean>
<!-- 2. conversion-service="conversionService":使用我们自己配置的类型转换组件 -->
<mvc:annotation-driven conversion-service="conversionService"/>
ConversionServiceFactoryBean创建的ConversionService组件是没有格式化器存在的
//只有转换功能
public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {
private Set<?> converters;
private GenericConversionService conversionService;
-----------------------------------------------------------------------------------------------------------
//转换+格式化
public class FormattingConversionServiceFactoryBean
implements FactoryBean<FormattingConversionService>, EmbeddedValueResolverAware, InitializingBean {
private Set<?> converters;
private Set<?> formatters;
private Set<FormatterRegistrar> formatterRegistrars;
private boolean registerDefaultFormatters = true;
private StringValueResolver embeddedValueResolver;
private FormattingConversionService conversionService;
4. 写 Controller,处理请求
@RequestMapping(value = "/addUser")
@ResponseBody
public String addUser(@RequestParam("userInfo") User user) {
System.out.println(user);
//其他操作 ...
return user.toString();
}
@RequestMapping("/addDate")
@ResponseBody
public String addDate(@RequestParam("date") Date date) {
System.out.println(date);
//其他操作 ...
return date.toString();
}
6. 测试
<form action="addUser">
<!--将用户的所有信息都写上,自动封装对象 -->
<input name="userInfo" value="张三-123456-18"/>
<input type="submit" value="测试User"/>
</form>
<form action="addDate">
<input name="date" value="2019-7-13"/>
<input type="submit" value="测试Date"/>
</form>
ConversionService converters =
java.lang.Boolean -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@47d21368
java.lang.Character -> java.lang.Number : org.springframework.core.convert.support.CharacterToNumberFactory@3cdad94b
java.lang.Character -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@2a65d058
java.lang.Enum -> java.lang.String : org.springframework.core.convert.support.EnumToStringConverter@5b63b99f
java.lang.Number -> java.lang.Character : org.springframework.core.convert.support.NumberToCharacterConverter@1fee86c
java.lang.Number -> java.lang.Number : org.springframework.core.convert.support.NumberToNumberConverterFactory@281e80cc
java.lang.Number -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@7cd83820
java.lang.String -> com.atguigu.bean.Employee : com.atguigu.component.MyStringToEmployeeConverter@3dc22c7a