Spring 3.1 M1:MVC 命名空间增强和@Configuration

工程 | Rossen Stoyanchev | 2011年2月21日 | ...

这是关于Spring 3.1 M1特性的系列文章的第五篇,本篇文章将关注Web应用。前半部分将讨论MVC XML命名空间的增强。然后将展示如何使用全Java配置来创建等效于MVC命名空间的内容。最后,我将提到您在3.1 M2中可以期待的一些与Servlet 3.0相关的配置更改。

MVC 命名空间改进

Spring MVC 3.0 提供了一个自定义的 MVC 命名空间。该命名空间的核心——<mvc:annotation-driven> 元素,配置了处理使用带注解的控制器方法的请求所需的一切。更重要的是,它设置了一系列默认值,这些默认值与类型转换、格式化、验证、读取和写入请求和响应的主体等有关。

随着时间的推移,你们很多人要求对上述默认配置的各个方面获得更多控制,我们在 3.1 M1 版本中解决了许多这些请求。

注册 Formatter

我们将从注册ConvertersFormatters 开始,这是通过提供您自己的ConversionService 实例来完成的,如下所示:


<mvc:annotation-driven conversion-service="..." />

对于自定义Formatter,您可以继承FormattingConversionServiceFactoryBean并在代码中注册Formatter。从3.1 M1开始,您可以使用setter声明性地注册FormatterAnnotationFormatterFactory类型。


<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>

您仍然可以选择在代码中注册ConvertersFormatters。这是使用本版本中引入的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对注册一个FormatterJodaTimeFormatterRegistrar是实际FormatterRegistrar实现的一个很好的例子,所以可以看看。

FormattingConversionServiceFactoryBean中的最后一个选项是能够通过registerDefaultFormatters标志完全关闭默认Formatter注册。

注册 HttpMessageConverters

从 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属性。

注册自定义 WebArgumentResolvers

如果您以前从未见过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

列表中的最后一个是能够提供自定义MessageCodesResolver的能力:


<mvc:annotation-driven message-codes-resolver="messageCodesResolver" />

<bean id="messageCodesResolver" class="org.example.CustomMessageCodesResolver" />

可以使用 MVC 命名空间执行许多其他操作。上述列表应有助于涵盖增强灵活性的最常见用例,但如果您认为我们错过了其他重要用例,请告诉我们。

从 XML 到 @Configuration

[侧边栏标题=更新]本节中的信息已过期。此方法在里程碑 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 包中创建MvcFeaturesMvcBeansMvcFeatures贡献@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。前者为我们提供了与后者相同的默认ConverterFormatter注册,但更适合与 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()方法中使用排除过滤器,以防止MvcFeaturesMvcBeans被注册两次——一次由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)。

我们希望您喜欢这些功能并发现它们有用。请告诉我们您的想法。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获取支持

Tanzu Spring在一个简单的订阅中提供对OpenJDK™、Spring和Apache Tomcat®的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部