2

Spring官方文档有专门一个章节阐述了BeanWrapper、DataBinder、ConversionService、Formatter

同时也有相关章节阐述了Spring MVC中对其的运用:

  1. Section 22.16.3, “Conversion and Formatting”

  2. Customizing data binding with @InitBinder

不过很遗憾,官方文档对于如何在Standalone app和Spring MVC中应用这些东西讲的并不是很明白,本文旨在对BeanWrapper、DataBinder、ConversionService、Formatter做一个简单的概念解释,有助于开发人员利用这些工具。

基础概念

以下先讲解释一下这四个东西分别是干嘛的。

BeanWrapper

BeanWrapper是一个方便开发人员使用字符串来对Java Bean的属性执行get、set操作的工具类。比如:

Foo foo = new Foo();
BeanWrapperImpl fooWrapper = new BeanWrapperImpl(foo);
fooWrapper.setPropertyValue("intProperty", "1");
Object intProperty = fooWrapper.getPropertyValue("intProperty");

它为那些UI类app提供了极大的便利,因为这类app大部分情况下都是以字符串和用户交互的,这个很好理解吧,用户看到的数据都是文字,而不是一堆二进制。

BeanWrapper内部使用了两种机制:

  1. PropertyEditor。PropertyEditor隶属于Java Bean规范。PropertyEditor只提供了String <-> Object的转换。

  2. ConversionService。Spring自3.0之后提供的替代PropertyEditor的机制,后面会详细说。

按照Spring官方文档的说法,当容器内没有注册ConversionService的时候,会退回使用PropertyEditor机制。

ConversionService

ConversionService及其相关一套类型转换机制是一套通用的类型转换SPI,相比PropertyEditor只提供String<->Object的转换,ConversionService能够提供任意Object<->Object的转换。

所以我们可以猜测,Spring为何要使用ConversionService替代PropertyEditor有三个原因:

  1. ConversionService功能更强大,支持的类型转换范围更广。

  2. ConverterFactory支持一整个class hierarchy的转换(也就是多态),PropertyEditor则不行。

  3. Java Bean这个规范最初是和Java GUI(Swing)一起诞生的,PropertyEditor接口里有大量和GUI相关的方法,显然已经过时了。顺便提一句,Java Bean和POJO不是一个概念,Java Bean不仅有setter、getter,还有一系列和Java GUI配套的东西。

Formatter

Formatter SPI是另外一套和PropertyEditor类似的,String<->Object的转换机制,但是有两个优点:

  1. 接口更干净,没有关于GUI的部分,只有printparse两个方法。

  2. 基于注解,支持同一类型的属性根据不同的格式来做String<->Object的转换。比如日期类型,一个字段的格式是yyyy-MM-dd,另一个格式是yyyyMMdd,如果利用PropertyEditor是比较麻烦,但是在这里就可以利用@DateTimeFormat来达到这个效果。

Spring提供了DefaultFormattingConversionService来支持Formatter SPI,也就是说如果要使用Formatter SPI,依然可以利用ConversionService接口。

注意:Formatter SPI必须基于注解才可以使用,这点和ConversionService基于类型不同。

DataBinder

DataBinder主要提供了两个功能:

  1. 利用BeanWrapper,给对象的属性设值

  2. 在设值的同时做Validation

因为Validation不在本文探讨范围只能因此不做详述了。

关系

本小节讲一下BeanWrapper、DataBinder、ConversionService、Formatter之间的关系,以及Spring内部对它们的使用。

四者关系图

图片描述

注意上图中ConversionService有两种实现:

  1. DefaultConversionService,不支持Formatter SPI

  2. DefaultFormattingConversionService,支持Formatter SPI

也就是说,如果要支持Formatter SPI,只需要让BeanWrapper切换使用不同的ConversionService即可。

Core Context的使用

图片描述

Spring Core Context其实也使用ConversionService,但是是非强制的。

Spring在读取xml配置文件的时候,因为xml文件实际上是一个文本文件,所有值的设置都是String,这个时候如果给bean的复杂类型属性设置值,它会用到PropertyEditor或ConversionService。

比如下面例子中的color属性是Color类型,这时就会利用到PropertyEditor和ConversionService:

<bean id="someBean" class="a.b.c.SomeBean">
    <property name="color" value="red"/>
</bean>

让Spring Core Context使用conversionService的方式很简单,配置一个名字叫做conversionService的Bean即可。详情见9.5.5 Configuring a ConversionService

需要注意的是,因为这个Bean是在非常早的时候就被使用的(AbstractApplicationContext#L834),因此它最好不要依赖过多的其他的Bean,避免造成启动失败。

MVC的使用

图片描述

Spring MVC对于conversionService的使用比较特殊,它自己会注册一个名字叫做mvcConversionService类型为DefaultFormattingConversionService的Bean(代码在WebMvcConfigurationSupport#mvcConversionService)。因此会存在以下陷阱:

  1. 如果Core Context中也定义了一个ConversionService,那么在MVC环境下,会有两个ConversionService的Bean。

  2. 针对Core Context的ConversionService做的Customize如FormatterRegistrarConverterRegistryFormatterRegistryConversionServiceFactoryBeanFormattingConversionServiceFactoryBean是不会应用到MVC的那个ConversionService上。 对于mvcConversionService的配置途径见这里


chanjarster
4.2k 声望244 粉丝