对象绑定与类型转换
底层第一套转换接口与实现
-
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());
}
}