虽然默认注册支持的Editor众多,但是依旧发现它并没有对Date类型、以及Jsr310提供的各种事件、日期类型的转换(当然也包括我们的自定义类型)。
因此我相信小伙伴都遇到过这样的痛点:Date、LocalDate等类型使用自动绑定老不方便了,并且还经常傻傻搞不清楚。所以最终很多都无奈选择了语义不是非常清晰的时间戳来传递
演示Date类型的数据绑定Demo:
@Getter @Setter @ToString public class Person { public String name; public Integer age; // 以Date类型为示例 private Date start; private Date end; private Date endTest; } public static void main(String[] args) { Person person = new Person(); DataBinder binder = new DataBinder(person, "person"); // 设置属性 MutablePropertyValues pvs = new MutablePropertyValues(); pvs.add("name", "fsx"); // 事件类型绑定 pvs.add("start", new Date()); pvs.add("end", "2019-07-20"); // 试用试用标准的事件日期字符串形式~ pvs.add("endTest", "Sat Jul 20 11:00:22 CST 2019"); binder.bind(pvs); System.out.println(person); }打印输出:
Person(name=fsx, age=null, start=Sat Jul 20 11:05:29 CST 2019, end=null, endTest=Sun Jul 21 01:00:22 CST 2019)结果是符合我预期的:start有值,end没有,endTest却有值。
可能小伙伴对start、end都可以理解,最诧异的是endTest为何会有值呢???
此处我简单解释一下处理步骤:
BeanWrapper调用setPropertyValue()给属性赋值,传入的value值都会交给convertForProperty()方法根据get方法的返回值类型进行转换~(比如此处为Date类型)
委托给this.typeConverterDelegate.convertIfNecessary进行类型转换(比如此处为string->Date类型)
先this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);找到一个合适的PropertyEditor(显然此处我们没有自定义Custom处理Date的PropertyEditor,返回null)
回退到使用ConversionService,显然此处我们也没有设置,返回null
回退到使用默认的editor = findDefaultEditor(requiredType);(注意:此处只根据类型去找了,因为上面说了默认不处理了Date,所以也是返回null)
最终的最终,回退到Spring对Array、Collection、Map的默认值处理问题,最终若是String类型,都会调用BeanUtils.instantiateClass(strCtor, convertedValue)也就是有参构造进行初始化~~~(请注意这必须是String类型才有的权利)
1. 所以本例中,到最后一步就相当于new Date("Sat Jul 20 11:00:22 CST 2019"),因为该字符串是标准的时间日期串,所以是阔仪的,也就是endTest是能被正常赋值的~
通过这个简单的步骤分析,解释了为何end没值,endTest有值了。
其实通过回退到的最后一步处理,我们还可以对此做巧妙的应用。比如我给出如下的一个巧用例子:
打印输出:
Person(name=fsx, child=Child(name=fsx-son, age=null))完美。
废话不多说,下面我通过自定义属性编辑器的手段,来让能够支持处理上面我们传入2019-07-20这种非标准的时间字符串。
我们知道DataBinder本身就是个PropertyEditorRegistry,因此我只需要自己注册一个自定义的PropertyEditor即可:
1、通过继承PropertyEditorSupport实现一个自己的处理Date的编辑器:
public class MyDatePropertyEditor extends PropertyEditorSupport { private static final String PATTERN = "yyyy-MM-dd"; @Override public String getAsText() { Date date = (Date) super.getValue(); return new SimpleDateFormat(PATTERN).format(date); } @Override public void setAsText(String text) throws IllegalArgumentException { try { super.setValue(new SimpleDateFormat(PATTERN).parse(text)); } catch (ParseException e) { System.out.println("ParseException...................."); } } }