对象绑定与类型转换
底层第一套转换接口与实现
-
Printer 把其它类型转为 String
-
Parser 把 String 转为其它类型
-
Formatter 综合 Printer 与 Parser 功能
-
Converter 把类型 S 转为类型 T
-
Printer、Parser、Converter 经过适配转换成 GenericConverter 放入 Converters 集合
-
FormattingConversionService 利用其它们实现转换
底层第二套转换接口
-
PropertyEditor 把 String 与其它类型相互转换
-
PropertyEditorRegistry 可以注册多个 PropertyEditor 对象
-
与第一套接口直接可以通过 FormatterPropertyEditorAdapter 来进行适配
高层接口与实现
-
它们都实现了 TypeConverter 这个高层转换接口,在转换时,会用到 TypeConverter Delegate 委派ConversionService 与 PropertyEditorRegistry 真正执行转换(Facade 门面模式)
-
首先看是否有自定义转换器, @InitBinder 添加的即属于这种 (用了适配器模式把 Formatter 转为需要的 PropertyEditor)
-
再看有没有 ConversionService 转换
-
再利用默认的 PropertyEditor 转换
-
最后有一些特殊处理
-
-
SimpleTypeConverter 仅做类型转换
-
BeanWrapperImpl 为 bean 的属性赋值,当需要时做类型转换,走 Property
-
DirectFieldAccessor 为 bean 的属性赋值,当需要时做类型转换,走 Field
-
ServletRequestDataBinder 为 bean 的属性执行绑定,当需要时做类型转换,根据 directFieldAccess 选择走 Property 还是 Field,具备校验与获取校验结果功能
类型转换与数据绑定
基本的类型转换与数据绑定用法
-
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(通过set,get方法来给属性赋值)
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);
}static class MyBean { private int a; private String b; private Date c; public int getA() { return a; } public void setA(int a) { this.a = a; } public String getB() { return b; } public void setB(String b) { this.b = b; } public Date getC() { return c; } public void setC(Date c) { this.c = c; } @Override public String toString() { return "MyBean{" + "a=" + a + ", b='" + b + '\'' + ", c=" + c + '}'; } }
}
-
DirectFieldAccessor(直接通过属性赋值)
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
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 + '}'; } }
}
数据绑定工厂
ServletRequestDataBinderFactory 的用法和扩展点
-
可以解析控制器的 @InitBinder 标注方法作为扩展点,添加自定义转换器
- 控制器私有范围
-
可以通过 ConfigurableWebBindingInitializer 配置 ConversionService 作为扩展点,添加自定义转换器
- 公共范围
-
同时加了 @InitBinder 和 ConversionService 的转换优先级
-
优先采用 @InitBinder 的转换器
-
其次使用 ConversionService 的转换器
-
使用默认转换器
-
特殊处理(例如有参构造)
public class TestServletDataBinderFactory {
public static void main(String[] args) throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("birthday", "1999|01|02");
request.setParameter("address.name", "西安");User target = new User(); // "1. 用工厂, 无转换功能"
// ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
// "2. 用 @InitBinder 转换" PropertyEditorRegistry PropertyEditor
// InvocableHandlerMethod method = new InvocableHandlerMethod(new MyController(), MyController.class.getMethod("aaa", WebDataBinder.class));
// ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(List.of(method), null);
// "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);
// "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(List.of(method), initializer);
// "5. 使用默认 ConversionService 转换"
ApplicationConversionService service = new ApplicationConversionService();
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)); System.out.println(target); } static class MyController { @InitBinder public void aaa(WebDataBinder dataBinder) { // 扩展 dataBinder 的转换器 dataBinder.addCustomFormatter(new MyDateFormatter("用 @InitBinder 方式扩展的")); } } public static class User { @DateTimeFormat(pattern = "yyyy|MM|dd") private Date birthday; private Address address; public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override public String toString() { return "User{" + "birthday=" + birthday + ", address=" + address + '}'; } } public static class Address { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Address{" + "name='" + name + '\'' + '}'; } }
}
-
在spring中使用ApplicationConversionService来对类型进行转换,无法识别的日期参数加注解 @DateTimeFormat,在jdk中使用DefaultConversionService,他也需要配合注解进行使用,
在这些默认的转换其中,并不需要 @InitBinder 转换ConversionService 转换。
获取泛型参数
-
java api 获取泛型参数
-
spring api 获取泛型参数
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(parameterizedType.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()); }
}