ROO中的ApplicationConversionServiceFactoryBean

问题提出

ROO项目中,第一次用web mvc命令新建一个controller后, ROO会在该controller所在的包中新建一个ApplicationConversionServiceFactoryBean类,这个类是干什么的呢?

线索

打开ApplicationConversionServiceFactoryBean.java文件,可得如下代码:

从这段代码出发,我们一步步搞清楚这个ApplicationConversionServiceFactoryBean

FormattingConversionServiceFactoryBean

ApplicationConversionServiceFactoryBean继承于FormattingConversionServiceFactoryBean,打开FormattingConversionServiceFactoryBean.java文件,可见如下代码:

先从它实现的接口出发,google了一下FactoryBeanFormattingConversionServiceEmbeddedValueResolverAwareInitializingBean这几个关键词:

FactoryBean

FactoryBean接口声明如下所示:

这里可以大致了解其用途:

Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean。工厂Bean跟普通Bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂Bean的getObject方法所返回的对象。
使用场景:1、通过外部对类是否是单例进行控制,该类自己无法感知 2、对类的创建之前进行初始化的操作,在afterPropertiesSet()中完成。

例如如下FactoryBean实现:

注册到容器:

若nextDayDateDisplayer类如下所示:

尽管注册时用的是NextDayDateFactoryBean类,但dateOfNextDay属性的类型是DateTime而不是NextDayDateFactoryBean。这就是说:

容器返回的是FactoryBean所”生产”的对象类型,而非FactoryBean实现本身。

所以,FactoryBean提供了一种高级的配置机制,令复杂的bean配置成为可能。

FormattingConversionService

之前写过一篇博文提到了spring mvc中数据类型转换、验证和格式化的流程,如图所示:

流程

从图中可以看出,Spring3引入了格式化转换器(Formatter SPI) 和格式化服务API(FormattingConversionService)来实现格式化(从Obejct到String),例如Date对象到String对象。该类实现了四个接口,分别是:EmbeddedValueResolverAware, ConversionService, ConverterRegistryFormatterRegistry

Formatter

先说一下Formatter接口,如下所示:

其中Printer和Parser接口分别定义了String print(T object, Locale locale);T parse(String text, Locale locale) throws ParseException;方法。

我们用的时候可以这样用

可以看出,Formatter接口包装了Object和String类型之间的转换,主要应用在把view层用户输入或选择的数据转换成controller层的实体类型。

Converter

Converter接口定义的方法如下所示:

若要实现多种类型之间的转换,可以用GenericConverter接口:

从上述可以看出,于Formatter不同,Converter负责任意类型之间的转换。

ConverterRegistry & FormatterRegistry

这两个接口主要用于用于注册格式化转换器

可以看出,ConverterRegistry 和 FormatterRegistry 声明了格式转换服务的注册接口。什么是“注册”?个人认为可以理解成spring在读取配置时,要将formatter等托管给某个统一的服务接口(facade模式?),这个服务接口由某个类来实现。而ConverterRegistry和FormatterRegistry尽管没有声明服务接口,但是给予了spring给该托管类注入formatter的能力。

ConversionService

参考api文档

A service interface for type conversion. This is the entry point into the convert system. Call convert(Object, Class) to perform a thread-safe type conversion using this system.

FormattingConversionService继承自ConversionService。而ConversionService接口声明了从任意类到任意类之间转换的接口:

故FormattingConversionService代表从String到Object或者从Object的转换,即格式化转换。

综上,再来看看FormattingConversionService。其一般使用方法如下

从该类的使用方法可以看出,ConversionService接口给了它转换类型的能力,ConverterRegistry和FormatterRegistry给了它被管理(注入)的能力。FormattingConversionService将不同的Formatter包装起来,对外提供统一的类型格式化服务接口。整个convert框架如下图所示:

框架

InitializingBean

google到这里

InitializingBean
Spirng的InitializingBean为bean提供了定义初始化方法的方式。InitializingBean是一个接口,它仅仅包含一个方法:afterPropertiesSet()。
Spring在设置完一个bean所有的合作者(依赖)后,会检查bean是否实现了InitializingBean接口,如果实现就调用bean的afterPropertiesSet方法。

可以看出InitializingBean接口的意义在于提供了一种bean装配完毕以后的自定义初始化入口。

EmbeddedValueResolverAware

请看这里

Interface to be implemented by any object that wishes to be notified of a StringValueResolver for the resolution of embedded definition values.
This is an alternative to a full ConfigurableBeanFactory dependency via the ApplicationContextAware/BeanFactoryAware interfaces.

留意到其中的StringValueResolver,google了一下,找到了这里

Simple strategy interface for resolving a String value. Used by ConfigurableBeanFactory.

这是什么意思呢?StringValueResolver是一个接口,resolve可以理解成“解析”,而spring在根据配置装配bean的时候,需要将配置中的String类型转换成其它类型,这个转换可以由实现来StringValueResolver接口的类来完成。

Aware的意思是“意识到的”,因此EmbeddedValueResolverAware接口的意思大概是某个bean实现了该接口,则在其被装配时,如果StringValueResolver被调用,则该resolver会通过void setEmbeddedValueResolver(StringValueResolver resolver)方法传入bean内部,用户可以在这里取得resolver。

综上,FormattingConversionServiceFactoryBean可以这样理解:

  1. 它是一个FactoryBean,在初始化时需要复杂的配置。
  2. 它“生成”一个FormattingConversionService实例,负责数据的格式化。
  3. 可以为它添加不同的formatter,令其支持不同的数据的格式化转换。
  4. afterPropertiesSet()方法在其初始化工作完成后被调用。

FormattingConversionServiceFactoryBean的实现

我们打开org.springframework.format.support.FormattingConversionServiceFactoryBean.java文件,可以观察到:

可以看出,FormattingConversionServiceFactoryBean提供了installFormatters()方法,使子类可以在父类完全生成后添加Formatter或者Converter。

ApplicationConversionServiceFactoryBean使用方法

默认的ApplicationConversionServiceFactoryBean实现中,只有一个空实现的方法:

从上文可以得知,installFormatters方法是FormattingConversionServiceFactoryBean为实现方便地增添formatter功能而暴露给子类的一个空方法,其在FormattingConversionServiceFactoryBean装配完成后被调用。打开ApplicationConversionServiceFactoryBean_Roo_ConversionService.aj文件,可见一系列如下代码:

这些方法都是由ROO自动根据项目中的Entity的设置自动生成的,返回包括从ID到Object,Object到String等一系列Converter。文件的最后有还有两个方法:

由此可以看出ApplicationConversionServiceFactoryBean的调用顺序为:

可以看出,getStringToSensorLayoutConverter()等一系列方法返回从某类到某类的converter,通过registry接口加入到service中,而installFormatters()方法暴露了一个“添加点”。所以,在自定义converter工厂方法后,要在installFormatters中调用registry.addConverter()完成注册。

ROO如何与ApplicationConversionServiceFactoryBean结合?

按ctrl+H,输入ApplicationConversionServiceFactoryBean进行全局文件搜索,在webmvc-config.xml文件中可见如下bean定义:

又见如下定义:

根据注解可以大概了解到这个的作用是令Spring MVC支持@Controller注解和注册了一系列Formatters和Validators用于@**Format和@vaild标签。那么这个究竟是什么意思呢?

这里提到,实际上声明了两个bean:DefaultAnnotationHandlerMappingAnnotationMethodHandlerAdapter

DefaultAnnotationHandlerMapping

这幅图 可以了解到,HandlerMapping负责url到controller的转发,而DefaultAnnotationHandlerMapping则负责

Implementation of the HandlerMapping interface that maps handlers based on portlet modes expressed through the RequestMapping annotation at the type or method level.

可以得知,DefaultAnnotationHandlerMapping实现了url到通过@RequestMapping注解标志的controller的转发功能。

AnnotationMethodHandlerAdapter

先说一下HandlerAdapterHandlerAdapterHandlerMapping的一个代理类,DispatcherServlet通过该代理来访问所有装配好的Handlers。 而AnnotationMethodHandlerAdapter从这里可以了解到:

Implementation of the HandlerAdapter interface that maps handler methods based on HTTP paths, HTTP methods and request parameters expressed through the RequestMapping annotation.

Supports request parameter binding through the RequestParam annotation. Also supports the ModelAttribute annotation for exposing model attribute values to the view, as well as InitBinder for binder initialization methods and SessionAttributes for automatic session management of specific attributes.

AnnotationMethodHandlerAdapter实现了handler转发请求过程中的参数绑定,@RequestParam、@ModelAttribute注解的解析。

综上,可以知道,<mvc:annotation-driven conversion-service="applicationConversionService"/>的意思是,实现了把请求转发到@controller注解标注的controller类,并解析了@RequestParam,@Valid等等标签,最后该标签的conversion-service属性指定了在转发请求中采用名为applicationConversionService的bean作为数据格式化,数据转换service。

总结

最后总结一下ApplicationConversionServiceFactoryBean的作用和调用流程:spring容器根据webmvc-config.xml进行初始化,根据<mvc:annotation-driven conversion-service="applicationConversionService"/>标签初始化了整个URLMapping和HTTP请求参数绑定、验证等工作,然后我们可以通过@RequestMapping来注册转发路径和通过@RequestParam等注解处理HTTP参数。该标签同时指定了ApplicationConversionServiceFactoryBean返回的ApplicationConversionService作为这些请求的数据格式化和数据转化的服务类,该服务类负责请求参数(通常为字符串)到实体的转换,所以我们能在controller的某个method里面直接取得实体类。我们可以通过修改或自定义Convertor来实现不同类型之间的转换,这种转换被注册到ApplicationConversionServiceFactoryBean之中,通常用来自定义实体类在view层的显示方式(转换成怎么样的字符串),也可以实现自定义form参数到实体类的转换方式。

发表评论

电子邮件地址不会被公开。