领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多这是关于Spring 3.1 M1特性的系列文章的第五篇,本篇文章将关注Web应用。前半部分将讨论MVC XML命名空间的增强。然后将展示如何使用全Java配置来创建等效于MVC命名空间的内容。最后,我将提到您在3.1 M2中可以期待的一些与Servlet 3.0相关的配置更改。
Spring MVC 3.0 提供了一个自定义的 MVC 命名空间。该命名空间的核心——<mvc:annotation-driven>
元素,配置了处理使用带注解的控制器方法的请求所需的一切。更重要的是,它设置了一系列默认值,这些默认值与类型转换、格式化、验证、读取和写入请求和响应的主体等有关。
随着时间的推移,你们很多人要求对上述默认配置的各个方面获得更多控制,我们在 3.1 M1 版本中解决了许多这些请求。
我们将从注册Converters
和 Formatters
开始,这是通过提供您自己的ConversionService
实例来完成的,如下所示:
<mvc:annotation-driven conversion-service="..." />
对于自定义Formatter,您可以继承FormattingConversionServiceFactoryBean
并在代码中注册Formatter。从3.1 M1开始,您可以使用setter声明性地注册Formatter
和AnnotationFormatterFactory
类型。
<mvc:annotation-driven conversion-service="conversionService" />
<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<list>
<bean class="org.example.EmailFormatter"/>
<bean class="org.example.PhoneAnnotationFormatterFactory"/>
</list>
</property>
</bean>
您仍然可以选择在代码中注册Converters
和 Formatters
。这是使用本版本中引入的FormatterRegistrar
接口完成的。这是一个示例:
public class FinancialInstrumentFormatterRegistry implements FormatterRegistrar {
public void registerFormatters(FormatterRegistry registry) {
// Register custom Converters and Formatters here...
}
}
这就是您的FormatterRegistrar
的连接方式:
<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatterRegistrars">
<list>
<bean class="org.example.FinancialInstrumentFormatterRegistrar"/>
</list>
</property>
</bean>
那么,何时应使用FormatterRegistrar
而不是 formatters setter 呢?当您需要从一个地方为特定格式类别注册多个相关的转换器和格式化程序时,FormatterRegistrar
非常有用。另一种情况是注册一个在与其自己的泛型类型<T>不同的字段类型下索引的Formatter
,或者可能从Printer
/Parser
对注册一个Formatter
。JodaTimeFormatterRegistrar
是实际FormatterRegistrar
实现的一个很好的例子,所以可以看看。
FormattingConversionServiceFactoryBean
中的最后一个选项是能够通过registerDefaultFormatters
标志完全关闭默认Formatter
注册。
从 3.1 M1 开始,您可以通过mvc:annotation-driven
的子元素注册HttpMessageConverters
。例如:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="com.google.protobuf.spring.http.ProtobufHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="prefixJson" value="true"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
以这种方式提供的消息转换器列表优先于 MVC 命名空间默认注册的消息转换器。例如,上面我们添加了一个自定义转换器ProtobufHttpMessageConverter
,并且我们还提供了一个根据应用程序需求自定义的 Spring MVC 的MappingJacksonHttpMessageConvert
实例。
如果您不希望默认注册任何消息转换器,请在<mvc:message-converters>
元素上使用register-defaults
属性。
如果您以前从未见过WebArgumentResolver
,它用于解析@RequestMapping
方法中的自定义参数。Spring Mobile项目有一个SitePreferenceWebArgumentResolver
。它解析SitePreference
方法参数类型,这些类型指示用户是否想要页面的移动版本或完整版本。从 Spring 3.1 M1 开始,您可以通过 MVC 命名空间注册自定义参数解析器:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.mobile.device.site.SitePreferenceWebArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
列表中的最后一个是能够提供自定义MessageCodesResolver
的能力:
<mvc:annotation-driven message-codes-resolver="messageCodesResolver" />
<bean id="messageCodesResolver" class="org.example.CustomMessageCodesResolver" />
可以使用 MVC 命名空间执行许多其他操作。上述列表应有助于涵盖增强灵活性的最常见用例,但如果您认为我们错过了其他重要用例,请告诉我们。
[侧边栏标题=更新]本节中的信息已过期。此方法在里程碑 2 中已更改。请改读这篇 Spring MVC 3.1 M2 文章。[/侧边栏]
在本部分文章中,我将采用一个现有的示例应用程序:mvc-showcase(你们许多人可能从Keith Donald之前的文章中熟悉它),并将其 XML 配置替换为基于 Java 的配置。这样做可以比较前后代码和配置。
生成的示例应用程序可在 spring-3.1-mvc-java-config 中签出。您可以在 GitHub 上直接浏览源代码,或者按照README说明在本地获取代码。
我们的第一步是修改 web.xml 以指向我们的基于 Java 的配置,并指定要使用哪个ApplicationContext
类型来处理该配置。以下是相关的 web.xml 片段:
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
org.springframework.samples.mvc.config.MvcFeatures
org.springframework.samples.mvc.config.MvcBeans
</param-value>
</init-param>
</servlet>
接下来,我们将在〜。config 包中创建MvcFeatures
和MvcBeans
。MvcFeatures
贡献@Feature
方法,这是我们的主要关注点:
@FeatureConfiguration
class MvcFeatures {
@Feature
public MvcAnnotationDriven annotationDriven(ConversionService conversionService) {
return new MvcAnnotationDriven().conversionService(conversionService)
.argumentResolvers(new CustomArgumentResolver());
}
// ...
}
上面的代码段等效于此 XML 命名空间配置:
<mvc:annotation-driven conversion-service="conversionService">
<mvc:argument-resolvers>
<bean class="org.springframework.samples.mvc.data.custom.CustomArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
正如您所看到的,MvcAnnotationDriven
使用方便的链式方法 API 提供与 XML 元素相同的选项。还要注意,我们声明了一个ConversionService
方法参数。此参数按类型自动装配并注入。其声明可在MvcBeans
中找到:
@Configuration
public class MvcBeans {
@Bean
public ConversionService conversionService() {
DefaultFormattingConversionService bean = new DefaultFormattingConversionService();
bean.addFormatterForFieldAnnotation(new MaskFormatAnnotationFormatterFactory());
return bean;
}
// ...
}
请注意使用DefaultFormattingConversionService
而不是通常在 XML 配置中使用的FormattingConversionServiceFactoryBean
。前者为我们提供了与后者相同的默认Converter
和Formatter
注册,但更适合与 Java 配置一起使用——它提供了一个简单的构造函数,而不是使用 XML 时 Spring 调用的FactoryBean
生命周期初始化方法。
MvcFeatures
的其余部分声明了<mvc:resources>
、<mvc:view-controller>
和<context:component-scan>
元素的等效项:
@FeatureConfiguration
class MvcFeatures {
// ...
@Feature
public MvcResources resources() {
return new MvcResources("/resources/**", "/resources/");
}
@Feature
public MvcViewControllers viewController() {
return new MvcViewControllers("/", "home");
}
@Feature
public ComponentScanSpec componentScan() {
return new ComponentScanSpec("org.springframework.samples").excludeFilters(
new AnnotationTypeFilter(Configuration.class),
new AnnotationTypeFilter(FeatureConfiguration.class));
}
}
有两点值得注意。一个是需要单个MvcViewControllers
实例来使用链式方法调用定义任意数量的视图控制器。第二个是在componentScan()
方法中使用排除过滤器,以防止MvcFeatures
和MvcBeans
被注册两次——一次由AnnotationConfigWebApplicationContext
注册,另一次由组件扫描注册。
为完整起见,这是MvcBeans
的其余部分:
@Configuration
public class MvcBeans {
// ...
@Bean
public InternalResourceViewResolver jspViewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setPrefix("/WEB-INF/views/");
bean.setSuffix(".jsp");
return bean;
}
@Bean
public MultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}
}
最后一步是删除位于/WEB-INF/spring
下的 Spring XML 配置。
就是这样。我们已经使用全基于 Java 的 Spring 配置启动了一个 Web 应用程序。现在已经引入了@FeatureConfiguration
和@Feature
,您可以期望看到越来越多的FeatureSpecification
实现作为自定义 XML 命名空间的替代方案。我相当喜欢 Java 配置的最终结果,但这当然并不意味着我的现有应用程序需要切换。这完全取决于选择。如果您更喜欢 XML 的声明性特性,并且您使用的是在 Spring XML 配置中对类和方法名称进行代码完成的 IDE,那么使用 XML 命名空间也可以。
正如最近在网络研讨会Introducing Spring Framework 3.1中听到的那样,在 Spring 3.1 的里程碑 2 中,您可以期待看到 Servlet 3.0 支持,包括与 AnnotationConfigWebApplicationContext 和在本系列博客文章中演示的环境抽象相结合的无 XML Web 应用程序设置(即没有 web.xml)。
我们希望您喜欢这些功能并发现它们有用。请告诉我们您的想法。