【第二十三讲】对象绑定与类型转换
- 两套底层转换接口,一套高层转换接口
- 类型转换和数据绑定示例
- 类型转换绑定与绑定器工厂
@DataTimeFormat
注解谁来解析Spring
提供的泛型操作技巧
1.两套底层转换接口,一套高层转换接口
底层第一套转换接口与实现
Printer
把其它类型转为String
Parser
把String
转为其它类型Formatter
综合Printer
与Parser
功能Converter
把类型 S 转为类型 TPrinter、Parser、Converter
经过适配转换成GenericConverter
放入Converters
集合FormattingConversionService
利用其它们实现转换
底层第二套转换接口
PropertyEditor
把 String 与其它类型相互转换PropertyEditorRegistry
可以注册多个PropertyEditor
对象- 与第一套接口直接可以通过
FormatterPropertyEditorAdapter
来进行适配
高层接口与实现
- 它们都实现了
TypeConverter
这个高层转换接口,在转换时,会用到TypeConverter Delegate
委派ConversionService
与PropertyEditorRegistry
真正执行转换**(Facade 门面模式)**- 首先看是否有自定义转换器,
@InitBinder
添加的即属于这种 (用了适配器模式把 Formatter 转为需要的PropertyEditor
) - 再看有没有
ConversionService
转换 - 再利用默认的
PropertyEditor
转换 - 最后有一些特殊处理
- 首先看是否有自定义转换器,
SimpleTypeConverter
仅做类型转换BeanWrapperImpl
为 bean 的属性赋值,当需要时做类型转换,走 PropertyDirectFieldAccessor
为 bean 的属性赋值,当需要时做类型转换,走 FieldServletRequestDataBinder
为 bean 的属性执行绑定,当需要时做类型转换,根据directFieldAccess
选择走 Property 还是 Field,具备校验与获取校验结果功能
2.类型转换和数据绑定示例
SimpleTypeConverter
public class TestSimpleConverter {
public static void main(String[] args) {
// 仅有类型转换的功能
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
Integer number = typeConverter.convertIfNecessary("13", int.class);
Date date = typeConverter.convertIfNecessary("1999/03/04", Date.class);
System.out.println(number);
System.out.println(date);
}
}
BeanWrapperImpl
public class TestBeanWrapper {
public static void main(String[] args) {
// 利用反射原理, 为 bean 的属性赋值
MyBean target = new MyBean();
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
wrapper.setPropertyValue("a", "10");
wrapper.setPropertyValue("b", "hello");
wrapper.setPropertyValue("c", "1999/03/04");
System.out.println(target);
}
@Data
static class MyBean {
private int a;
private String b;
private Date c;
}
}
DirectFieldAccessor
不需要get set 方法
public class TestFieldAccessor {
public static void main(String[] args) {
// 利用反射原理, 为 bean 的属性赋值
MyBean target = new MyBean();
DirectFieldAccessor accessor = new DirectFieldAccessor(target);
accessor.setPropertyValue("a", "10");
accessor.setPropertyValue("b", "hello");
accessor.setPropertyValue("c", "1999/03/04");
System.out.println(target);
}
static class MyBean {
private int a;
private String b;
private Date c;
@Override
public String toString() {
return "MyBean{" +
"a=" + a +
", b='" + b + '\'' +
", c=" + c +
'}';
}
}
}
ServletRequestDataBinder
directFieldAccess
选择走 Property 还是 Field
public class TestDataBinder {
public static void main(String[] args) {
// 执行数据绑定
MyBean target = new MyBean();
DataBinder dataBinder = new DataBinder(target);
dataBinder.initDirectFieldAccess();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("a", "10");
pvs.add("b", "hello");
pvs.add("c", "1999/03/04");
dataBinder.bind(pvs);
System.out.println(target);
}
static class MyBean {
private int a;
private String b;
private Date c;
@Override
public String toString() {
return "MyBean{" +
"a=" + a +
", b='" + b + '\'' +
", c=" + c +
'}';
}
}
}
3.类型转换绑定与绑定器工厂
绑定器工厂
日期绑定不成功
public class TestServletDataBinderFactory {
public static void main(String[] args) {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("birthday", "1999|01|02");
request.setParameter("address.name", "xi.an");
User target = new User();
ServletRequestDataBinder dataBinder = new ServletRequestDataBinder(target);
dataBinder.bind(new ServletRequestParameterPropertyValues(request));
System.out.println("--"+target);
}
@Data
public static class User {
private Date birthday;
private Address address;
}
@Data
public static class Address {
private String name;
}
}
1.使用工厂,无转化功能
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
WebDataBinder dataBinder = factory.createBinder(new ServletWebRequest(request), target, "user");
1. 用 @InitBinder 转换
public static void main(String[] args) throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("birthday", "1999|01|02");
request.setParameter("address.name", "xi.an");
User target = new User();
// "2. 用 @InitBinder 转换" PropertyEditorRegistry PropertyEditor
InvocableHandlerMethod method = new InvocableHandlerMethod(new MyController(), MyController.class.getMethod("aaa", WebDataBinder.class));
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(Arrays.asList(method), null);
WebDataBinder dataBinder = factory.createBinder(new ServletWebRequest(request), target, "user");
dataBinder.bind(new ServletRequestParameterPropertyValues(request));
System.out.println("--"+target);
}
MyController
static class MyController {
@InitBinder
public void aaa(WebDataBinder dataBinder) {
// 扩展 dataBinder 的转换器
dataBinder.addCustomFormatter(new MyDateFormatter("用 @InitBinder 方式扩展的"));
}
}
自定义MyDateFormatter
@Slf4j
public class MyDateFormatter implements Formatter<Date> {
private final String desc;
public MyDateFormatter(String desc) {
this.desc = desc;
}
@Override
public String print(Date date, Locale locale) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
return sdf.format(date);
}
@Override
public Date parse(String text, Locale locale) throws ParseException {
log.debug(">>>>>> 进入了: {}", desc);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
return sdf.parse(text);
}
}
2.用 ConversionService
转换"
//"3. 用 ConversionService 转换" ConversionService Formatter
FormattingConversionService service = new FormattingConversionService();
service.addFormatter(new MyDateFormatter("用 ConversionService 方式扩展转换功能"));
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
initializer.setConversionService(service);
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, initializer);
WebDataBinder dataBinder = factory.createBinder(new ServletWebRequest(request), target, "user");
dataBinder.bind(new ServletRequestParameterPropertyValues(request));
3.同时加了 @InitBinder 和 ConversionService"
// "4. 同时加了 @InitBinder 和 ConversionService"
InvocableHandlerMethod method = new InvocableHandlerMethod(new MyController(), MyController.class.getMethod("aaa", WebDataBinder.class));
FormattingConversionService service = new FormattingConversionService();
service.addFormatter(new MyDateFormatter("用 ConversionService 方式扩展转换功能"));
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
initializer.setConversionService(service);
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(Arrays.asList(method), initializer);
WebDataBinder dataBinder = factory.createBinder(new ServletWebRequest(request), target, "user");
dataBinder.bind(new ServletRequestParameterPropertyValues(request));
优先使用InitBinder
4.使用默认 ConversionService
转换"
5.Spring 提供的泛型操作技巧
1.jdk api
2.Spring api
BaseDao
class BaseDao<T> {
T findOne() {
return null;
}
}
--------------------------------------------
class StudentDao extends BaseDao<Student> {
}
--------------------------------------------
class Student {
}
----------------------------------------------
class TeacherDao extends BaseDao<Teacher> {
}
---------------------------------------------
class EmployeeDao extends BaseDao {
}
获取泛型参数
public class TestGenericType {
public static void main(String[] args) {
// 小技巧
// 1. java api
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>");
Type type = TeacherDao.class.getGenericSuperclass();
System.out.println(type);
if (type instanceof ParameterizedType parameterizedType) {
System.out.println(
.getActualTypeArguments()[0]);
}
// 2. spring api 1
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>");
Class<?> t = GenericTypeResolver.resolveTypeArgument(TeacherDao.class, BaseDao.class);
System.out.println(t);
// 3. spring api 2
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>");
System.out.println(ResolvableType.forClass(TeacherDao.class).getSuperType().getGeneric().resolve());
}
}
总结
ServletRequestDataBinderFactory 的用法和扩展点
- 可以解析控制器的 @InitBinder 标注方法作为扩展点,添加自定义转换器
- 控制器私有范围
- 可以通过 ConfigurableWebBindingInitializer 配置 ConversionService 作为扩展点,添加自定义转换器
- 公共范围
- 同时加了 @InitBinder 和 ConversionService 的转换优先级
- 优先采用 @InitBinder 的转换器
- 其次使用 ConversionService 的转换器
- 使用默认转换器
- 特殊处理(例如有参构造)