当前位置 : 主页 > 编程语言 > java >

DataBinder

来源:互联网 收集:自由互联 发布时间:2023-03-22
前言 数据绑定 这个概念在任何一个成型的框架中都是特别重要的(尤其是web框架),它能让框架更多的自动化,更好容错性以及更高的编码效率。它提供的能力是:把字符串形式的参

前言

数据绑定 这个概念在任何一个成型的框架中都是特别重要的(尤其是web框架),它能让框架更多的自动化,更好容错性以及更高的编码效率。它提供的能力是:把字符串形式的参数转换成服务端真正需要的类型的转换(当然可能还包含校验)。

对​​Spring​​​中的数据绑定场景,小伙伴们就再熟悉不过了。比如我们​​Controller​​​中只需要使用​​Model​​​对象就能完成​​request​​到Model对象的自动数据自动绑定,使用起来着实非常方便~(完全屏蔽了Servlet的API)

既然数据绑定这么重要,但是为何鲜有人提起呢?我也上网搜了搜关于​​DataBinder​​​的相关资料,相对来说还是寥寥无几的~ 我们不提起并不代表它不重要,这些都是Spring它帮我们默默的干了。这也印证了那句名言嘛:我们的安好是因为有人替我们负重前行

查到网上的资料,大都停留在如何使用​​WebDataBinder​​​的说明上,并且几乎没有文章是专门分析核心部件​​DataBinder​​的,本文作为此方面的一股清流,在此把我结合官方文档、源码的所获分享给大家~

DataBinder

注意,此类所在的包是​​org.springframework.validation​​,所以可想而知,它不仅仅完成数据的绑定,还会和数据校验有关~

注意:我看到有的文章说​​DataBinder​​​在绑定的时候还会进行数据校验Validation,其实这个是不准确的,容易误导人(校验动作不发生在​​DataBinder​​本类)

还有说​​DataBinder​​​数据绑定最终依赖的是​​BeanWrapper​​​,其实这个也是不准确的,实际上依赖的是​​PropertyAccessor​​。

DataBinder使用Demo

先看一个简单​​Demo​​,体验一把直接使用​​DataBinder​​进行数据绑定吧:

public static void main(String[] args) throws BindException { Person person = new Person(); DataBinder binder = new DataBinder(person, "person"); MutablePropertyValues pvs = new MutablePropertyValues(); pvs.add("name", "fsx"); pvs.add("age", 18); binder.bind(pvs); Map<?, ?> close = binder.close(); System.out.println(person); System.out.println(close); }

输出:

Person{name='fsx', age=18}{person=Person{name='fsx', age=18}, org.springframework.validation.BindingResult.person=org.springframework.validation.BeanPropertyBindingResult: 0 errors}

其实​​Spring​​一直是在弱化数据绑定对使用者的接触(这就是为何鲜有人提起的原因),所以之前博文也说到​​Spring​​​并不推荐直接使用​​BeanWrapper​​去自己绑定数据(而是都让框架自己来完成吧~)。

​​BeanWrapper​​​不推荐直接使用,但是​​DataBinder​​​是一个更为​​成熟、完整​​​些的数据绑定器,若实在有需求使用它是比使用​​BeanWrapper​​是个更好的选择~

其实直接使用顶层的​​DataBinder​​​也是一般不会的,而是使用它的子类。比如web包下大名鼎鼎的​​WebDataBinder​​~

源码分析

​​DataBinder​​​的源码相对来说还是颇为复杂的,它提供的能力非常强大,也注定了它的方法非常多、属性也非常多。 首先看看类声明:

public class DataBinder implements PropertyEditorRegistry, TypeConverter {}

它是个实现类,直接实现了​​PropertyEditorRegistry​​​和​​TypeConverter​​​这两个接口,因此它可以注册​​java.beans.PropertyEditor​​​,并且能完成类型转换(​​TypeConverter​​)。

关于数据转换这块内容,有兴趣的可参见:​​【小家Spring】聊聊Spring中的数据转换:Converter、ConversionService、TypeConverter、PropertyEditor​​

接下里分析具体源码(需要解释说明都写在源码处了):

public class DataBinder implements PropertyEditorRegistry, TypeConverter { /** Default object name used for binding: "target". */ public static final String DEFAULT_OBJECT_NAME = "target"; /** Default limit for array and collection growing: 256. */ public static final int DEFAULT_AUTO_GROW_COLLECTION_LIMIT = 256; @Nullable private final Object target; private final String objectName; // 默认值是target // BindingResult:绑定错误、失败的时候会放进这里来~ @Nullable private AbstractPropertyBindingResult bindingResult; //类型转换器,会注册最为常用的那么多类型转换Map<Class<?>, PropertyEditor> defaultEditors @Nullable private SimpleTypeConverter typeConverter; // 默认忽略不能识别的字段~ private boolean ignoreUnknownFields = true; // 不能忽略非法的字段(比如我要Integer,你给传aaa,那肯定就不让绑定了,抛错) private boolean ignoreInvalidFields = false; // 默认是支持级联的~~~ private boolean autoGrowNestedPaths = true; private int autoGrowCollectionLimit = DEFAULT_AUTO_GROW_COLLECTION_LIMIT; // 这三个参数 都可以自己指定~~ 允许的字段、不允许的、必须的 @Nullable private String[] allowedFields; @Nullable private String[] disallowedFields; @Nullable private String[] requiredFields; // 转换器ConversionService @Nullable private ConversionService conversionService; // 状态码处理器~ @Nullable private MessageCodesResolver messageCodesResolver; // 绑定出现错误的处理器~ private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor(); // 校验器(这个非常重要) private final List<Validator> validators = new ArrayList<>(); // objectName没有指定,就用默认的 public DataBinder(@Nullable Object target) { this(target, DEFAULT_OBJECT_NAME); } public DataBinder(@Nullable Object target, String objectName) { this.target = ObjectUtils.unwrapOptional(target); this.objectName = objectName; } ... // 省略所有属性的get/set方法 // 提供一些列的初始化方法,供给子类使用 或者外部使用 下面两个初始化方法是互斥的 public void initBeanPropertyAccess() { Assert.state(this.bindingResult == null, "DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods"); this.bindingResult = createBeanPropertyBindingResult(); } protected AbstractPropertyBindingResult createBeanPropertyBindingResult() { BeanPropertyBindingResult result = new BeanPropertyBindingResult(getTarget(), getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit()); if (this.conversionService != null) { result.initConversion(this.conversionService); } if (this.messageCodesResolver != null) { result.setMessageCodesResolver(this.messageCodesResolver); } return result; } // 你会发现,初始化DirectFieldAccess的时候,校验的也是bindingResult ~~~~ public void initDirectFieldAccess() { Assert.state(this.bindingResult == null, "DataBinder is already initialized - call initDirectFieldAccess before other configuration methods"); this.bindingResult = createDirectFieldBindingResult(); } protected AbstractPropertyBindingResult createDirectFieldBindingResult() { DirectFieldBindingResult result = new DirectFieldBindingResult(getTarget(), getObjectName(), isAutoGrowNestedPaths()); if (this.conversionService != null) { result.initConversion(this.conversionService); } if (this.messageCodesResolver != null) { result.setMessageCodesResolver(this.messageCodesResolver); } return result; } ... // 把属性访问器返回,PropertyAccessor(默认直接从结果里拿),子类MapDataBinder有复写 protected ConfigurablePropertyAccessor getPropertyAccessor() { return getInternalBindingResult().getPropertyAccessor(); } // 可以看到简单的转换器也是使用到了conversionService的,可见conversionService它的效用 protected SimpleTypeConverter getSimpleTypeConverter() { if (this.typeConverter == null) { this.typeConverter = new SimpleTypeConverter(); if (this.conversionService != null) { this.typeConverter.setConversionService(this.conversionService); } } return this.typeConverter; } ... // 省略众多get方法 // 设置指定的可以绑定的字段,默认是所有字段~~~ // 例如,在绑定HTTP请求参数时,限制这一点以避免恶意用户进行不必要的修改。 // 简单的说:我可以控制只有指定的一些属性才允许你修改~~~~ // 注意:它支持xxx*,*xxx,*xxx*这样的通配符 支持[]这样子来写~ public void setAllowedFields(@Nullable String... allowedFields) { this.allowedFields = PropertyAccessorUtils.canonicalPropertyNames(allowedFields); } public void setDisallowedFields(@Nullable String... disallowedFields) { this.disallowedFields = PropertyAccessorUtils.canonicalPropertyNames(disallowedFields); } // 注册每个绑定进程所必须的字段。 public void setRequiredFields(@Nullable String... requiredFields) { this.requiredFields = PropertyAccessorUtils.canonicalPropertyNames(requiredFields); if (logger.isDebugEnabled()) { logger.debug("DataBinder requires binding of required fields [" + StringUtils.arrayToCommaDelimitedString(requiredFields) + "]"); } } ... // 注意:这个是set方法,后面是有add方法的~ // 注意:虽然是set,但是引用是木有变的~~~~ public void setValidator(@Nullable Validator validator) { // 判断逻辑在下面:你的validator至少得支持这种类型呀 哈哈 assertValidators(validator); // 因为自己手动设置了,所以先清空 再加进来~~~ // 这步你会发现,即使validator是null,也是会clear的哦~ 符合语意 this.validators.clear(); if (validator != null) { this.validators.add(validator); } } private void assertValidators(Validator... validators) { Object target = getTarget(); for (Validator validator : validators) { if (validator != null && (target != null && !validator.supports(target.getClass()))) { throw new IllegalStateException("Invalid target for Validator [" + validator + "]: " + target); } } } public void addValidators(Validator... validators) { assertValidators(validators); this.validators.addAll(Arrays.asList(validators)); } // 效果同set public void replaceValidators(Validator... validators) { assertValidators(validators); this.validators.clear(); this.validators.addAll(Arrays.asList(validators)); } // 返回一个,也就是primary默认的校验器 @Nullable public Validator getValidator() { return (!this.validators.isEmpty() ? this.validators.get(0) : null); } // 只读视图 public List<Validator> getValidators() { return Collections.unmodifiableList(this.validators); } // since Spring 3.0 public void setConversionService(@Nullable ConversionService conversionService) { Assert.state(this.conversionService == null, "DataBinder is already initialized with ConversionService"); this.conversionService = conversionService; if (this.bindingResult != null && conversionService != null) { this.bindingResult.initConversion(conversionService); } } // =============下面它提供了非常多的addCustomFormatter()方法 注册进PropertyEditorRegistry里===================== public void addCustomFormatter(Formatter<?> formatter); public void addCustomFormatter(Formatter<?> formatter, String... fields); public void addCustomFormatter(Formatter<?> formatter, Class<?>... fieldTypes); // 实现接口方法 public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor); public void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String field, PropertyEditor propertyEditor); ... // 实现接口方法 // 统一委托给持有的TypeConverter~~或者是getInternalBindingResult().getPropertyAccessor();这里面的 @Override @Nullable public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable MethodParameter methodParam) throws TypeMismatchException { return getTypeConverter().convertIfNecessary(value, requiredType, methodParam); } // ===========上面的方法都是开胃小菜,下面才是本类最重要的方法============== // 该方法就是把提供的属性值们,绑定到目标对象target里去~~~ public void bind(PropertyValues pvs) { MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues ? (MutablePropertyValues) pvs : new MutablePropertyValues(pvs)); doBind(mpvs); } // 此方法是protected的,子类WebDataBinder有复写~~~加强了一下 protected void doBind(MutablePropertyValues mpvs) { // 前面两个check就不解释了,重点看看applyPropertyValues(mpvs)这个方法~ checkAllowedFields(mpvs); checkRequiredFields(mpvs); applyPropertyValues(mpvs); } // allowe允许的 并且还是没有在disallowed里面的 这个字段就是被允许的 protected boolean isAllowed(String field) { String[] allowed = getAllowedFields(); String[] disallowed = getDisallowedFields(); return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) && (ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field))); } ... // protected 方法,给target赋值~~~~ protected void applyPropertyValues(MutablePropertyValues mpvs) { try { // 可以看到最终赋值 是委托给PropertyAccessor去完成的 getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields()); // 抛出异常,交给BindingErrorProcessor一个个处理~~~ } catch (PropertyBatchUpdateException ex) { for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) { getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult()); } } } // 执行校验,此处就和BindingResult 关联上了,校验失败的消息都会放进去(不是直接抛出异常哦~ ) public void validate() { Object target = getTarget(); Assert.state(target != null, "No target to validate"); BindingResult bindingResult = getBindingResult(); // 每个Validator都会执行~~~~ for (Validator validator : getValidators()) { validator.validate(target, bindingResult); } } // 带有校验提示的校验器。SmartValidator // @since 3.1 public void validate(Object... validationHints) { ... } // 这一步也挺有意思:实际上就是若有错误,就抛出异常 // 若没错误 就把绑定的Model返回~~~(可以看到BindingResult里也能拿到最终值哦~~~) // 此方法可以调用,但一般较少使用~ public Map<?, ?> close() throws BindException { if (getBindingResult().hasErrors()) { throw new BindException(getBindingResult()); } return getBindingResult().getModel(); }}

从源源码的分析中,大概能总结到​​DataBinder​​它提供了如下能力:

  • 把属性值​​PropertyValues​​​绑定到target上(​​bind()​​​方法,依赖于​​PropertyAccessor​​实现)</li><li>提供校验的能力:提供了public方法<code>validate()</code>对各个属性使用<code>Validator</code>执行校验
  • 提供了注册属性编辑器(​​PropertyEditor​​​)和对类型进行转换的能力(​​TypeConverter​​)
  • 还需要注意的是:

  • ​​initBeanPropertyAccess​​​和​​initDirectFieldAccess​​​两个初始化​​PropertyAccessor​​​方法是互斥的 1. ​​​initBeanPropertyAccess()​​​创建的是​​BeanPropertyBindingResult​​​,内部依赖​​BeanWrapper​​​ 2. ​​initDirectFieldAccess​​​创建的是​​DirectFieldBindingResult​​​,内部依赖​​DirectFieldAccessor​​
  • 这两个方法内部没有显示的调用,但是Spring内部默认使用的是​​initBeanPropertyAccess()​​​,具体可以参照​​getInternalBindingResult()​​方法~
  • 总结

    本文介绍了​​Spring​​​用于数据绑定的最直接类​​DataBinder​​​,它位于​​spring-context​​​这个工程的​​org.springframework.validation​​包内,所以需要再次明确的是:它是Spring提供的能力而非web提供的~

    虽然我们​​DataBinder​​​是Spring提供,但其实把它发扬光大是发生在Web环境,也就是大名鼎鼎的​​WebDataBinder​​,毕竟我们知道一般只有进行web交互的时候,才会涉及到字符串 -&gt; Java类型/对象的转换,这就是下个章节讲述的重中之重~

    网友评论