第22.2.1节“WebApplicationContext中的特殊Bean类型”和第22.2.2节“默认DispatcherServlet配置”介绍了Spring MVC的特殊bean以及DispatcherServlet
使用的默认实现。在本节中,您将了解到配置Spring MVC的另外两种方法。即MVC Java配置和MVC XML命名空间。
MVC Java配置和MVC命名空间提供类似的默认配置,可以覆盖DispatcherServlet
默认值。目标是使大多数应用程序不必创建相同的配置,并提供更高级别的构造,用于配置作为简单起始点的Spring MVC,并且几乎不需要基础配置的先前知识。
您可以根据自己的喜好选择MVC Java配置或MVC命名空间。另外,您将在下面进一步看到,使用MVC Java配置,可以更容易地查看底层配置,以及直接对创建的Spring MVC bean进行细粒度的定制。但是让我们从一开始就开始。
22.16.1启用MVC Java Config或MVC XML命名空间
要启用MVC Java配置,将@EnableWebMvc
注释添加到您的一个@Configuration
类中
@Configuration
@EnableWebMvc
public class WebConfig {
}
要在XML中实现相同的操作,请使用DispatcherServlet
上下文中的mvc:annotation-driven
元素(如果您没有定义DispatcherServlet
上下文,则在根上下文中):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven/>
</beans>
以上注册了一个RequestMappingHandlerMapping
,一个RequestMappingHandlerAdapter
和一个ExceptionHandlerExceptionResolver
(以及其他),以支持使用注释(如@RequestMapping
,@ExceptionHandler
等)的注释控制器方法处理请求。
它还支持以下功能:
除了用于数据绑定的JavaBeans
PropertyEditor
之外,还可以通过ConversionService
实例进行Spring 3风格类型转换。支持格式化通过
ConversionService
使用@NumberFormat
注释的Number
字段。支持使用
@DateTimeFormat
注释格式化日期,日历,长和和达时间字段。如果类路径中存在
JSR-303
提供程序,则支持使用@Valid验证@Controller
输入。HttpMessageConverter
支持@RequestBody
方法参数和@ResponseBody
方法从@RequestMapping
或@ExceptionHandler
方法返回值。
这是由mvc设计的HttpMessageConverters
的完整列表:annotation-driven
:
ByteArrayHttpMessageConverter
转换字节数组。StringHttpMessageConverter
转换字符串。ResourceHttpMessageConverter
转换为/从org.springframework.core.io.Resource
为所有媒体类型。SourceHttpMessageConverter
转换为/从javax.xml.transform.Source
转换。FormHttpMessageConverter
将表单数据转换为MultiValueMap <String,String>
。Jaxb2RootElementHttpMessageConverter
将Java对象转换为/从XML中添加 - 如果存在JAXB2,并且类别路径中不存在Jackson 2 XML扩展名。MappingJackson2HttpMessageConverter
转换为/从JSON添加,如果Jackson 2存在于类路径中。MappingJackson2XmlHttpMessageConverter
转换为/从XML中添加,如果Jackson 2 XML扩展存在于类路径中。AtomFeedHttpMessageConverter
转换Atom feed
- 如果罗马存在于类路径中,则会被添加。RssChannelHttpMessageConverter
转换RSS源 - 如果罗马存在于类路径中则添加。
有关如何自定义这些默认转换器的更多信息,请参见第22.16.12节“消息转换器”。
Jackson JSON和XML转换器使用由Jackson2ObjectMapperBuilder
创建的ObjectMapper实例创建,以提供更好的默认配置。
此构建器使用以下命令自定义Jackson的默认属性:
-
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
被禁用。 -
MapperFeature.DEFAULT_VIEW_INCLUSION
被禁用。
如果在类路径中检测到它们,它还会自动注册以下众所周知的模块: -
jackson-datatype-jdk7
:支持Java 7类型,如java.nio.file.Path。 -
jackson-datatype-joda
:支持Joda-Time类型。 -
jackson-datatype-jsr310
:支持Java 8 Date&Time API类型。 -
jackson-datatype-jdk8
:支持其他Java 8类型,如可选。
22.16.2定制提供的配置
要自定义Java中的默认配置,您只需实现WebMvcConfigurer
接口,或者更有可能扩展WebMvcConfigurerAdapter
类并覆盖所需的方法:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
// Override configuration methods...
}
要自定义<mvc:annotation-driven />
的默认配置,请检查它支持哪些属性和子元素。 您可以查看Spring MVC XML模式或使用IDE的代码完成功能来发现哪些属性和子元素可用。
22.16.3转换和格式化
默认情况下,安装了Number和Date类型的格式化程序,包括对@NumberFormat
和@DateTimeFormat
注释的支持。 如果Joda Time存在于类路径中,也会安装Joda Time格式化库的完整支持。 要注册自定义格式化程序和转换器,请覆盖addFormatters方法:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addFormatters(FormatterRegistry registry) {
// Add formatters and/or converters
}
}
在MVC命名空间中,当添加<mvc:annotation-driven>
时,默认值也适用。 要注册自定义格式化程序和转换器,只需提供一个ConversionService
:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="org.example.MyConverter"/>
</set>
</property>
<property name="formatters">
<set>
<bean class="org.example.MyFormatter"/>
<bean class="org.example.MyAnnotationFormatterFactory"/>
</set>
</property>
<property name="formatterRegistrars">
<set>
<bean class="org.example.MyFormatterRegistrar"/>
</set>
</property>
</bean>
</beans>
有关何时使用
FormatterRegistrars
的更多信息,请参见第9.6.4节“FormatterRegistrar SPI”和FormattingConversionServiceFactoryBean。
22.16.4 Validation
Spring提供了一个Validator接口,可用于在应用程序的所有层中进行验证。 在Spring MVC中,您可以将其配置为用作全局Validator实例,以在遇到@Valid
或@Validated
控制器方法参数时使用,和/或通过@InitBinder
方法作为控制器中的本地验证器。 全局和本地验证器实例可以组合以提供复合验证。
Spring还通过LocalValidatorFactoryBean
支持JSR-303 / JSR-349 Bean
验证,它将Springorg.springframework.validation.Validator
接口适配为Bean验证javax.validation.Validator
合同。 这个类可以插入Spring MVC作为下面描述的全局验证器。
默认情况下,在类路径中检测到一个Bean验证提供程序(如Hibernate Validator)时,@EnableWebMvc
或<mvc:annotation-driven>
会在Spring MVC中通过LocalValidatorFactoryBean
自动注册Bean验证支持。
或者,您可以配置自己的全局Validator实例:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public Validator getValidator(); {
// return "global" validator
}
}
和XML:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven validator="globalValidator"/>
</beans>
要将全局和本地验证结合起来,只需添加一个或多个本地验证器:
@Controller
public class MyController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new FooValidator());
}
}
使用这个最小配置,任何时候遇到@Valid
或@Validated
方法参数,它将被配置的验证器验证。 任何验证违规将自动暴露为BindingResult
中的错误,作为方法参数可访问,并且可在Spring MVC HTML视图中呈现。
22.16.5拦截器
您可以将HandlerInterceptors
或WebRequestInterceptors
配置为适用于所有传入请求或限制到特定URL路径模式。
在Java中注册拦截器的示例:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleInterceptor());
registry.addInterceptor(new ThemeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
}
}
在XML中使用<mvc:interceptors>元素:
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/admin/**"/>
<bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/secure/*"/>
<bean class="org.example.SecurityInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
22.16.6 Content Negotiation
您可以配置Spring MVC如何根据请求确定所请求的介质类型。 可用的选项是检查文件扩展名的URL路径,检查“接受”标题,特定查询参数,或者在没有请求时返回默认内容类型。 默认情况下,首先检查请求URI中的路径扩展名,并检查“Accept”头。
默认情况下,MVC Java配置和MVC命名空间注册json
,xml
,rss
,atom
,如果相应的依赖关系在类路径上。 额外的路径扩展到媒体类型的映射也可以被明确地注册,并且还具有将其作为安全扩展白名单作为RFD攻击检测的作用的效果(更详细地参见“后缀模式匹配和RFD”一节)。
以下是通过MVC Java配置定制内容协商选项的示例:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.mediaType("json", MediaType.APPLICATION_JSON);
}
}
在MVC命名空间中,<mvc:annotation-driven>
元素具有content-negotiation-manager
属性,该属性需要ContentNegotiationManager
,而ContentNegotiationManager
又可以通过ContentNegotiationManagerFactoryBean
创建:
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes">
<value>
json=application/json
xml=application/xml
</value>
</property>
</bean>
如果不使用MVC Java配置或MVC命名空间,则需要创建一个ContentNegotiationManager
的实例,并使用它来配置RequestMappingHandlerMapping
以进行请求映射,以及RequestMappingHandlerAdapter
和ExceptionHandlerExceptionResolver
进行内容协商。
请注意,ContentNegotiatingViewResolver
现在也可以配置ContentNegotiationManager
,因此您可以在Spring MVC中使用一个共享实例。
在更高级的情况下,配置多个ContentNegotiationManager
实例可能有用,而这些实例又可能包含自定义的ContentNegotiationStrategy
实现。 例如,您可以使用ContentNegotiationManager
配置ExceptionHandlerExceptionResolver
,它始终将请求的介质类型解析为“application / json”
。 或者,如果没有请求内容类型,您可能希望插入具有某种逻辑选择默认内容类型(例如,XML或JSON)的自定义策略。
22.16.7查看控制器
这是一个快捷方式,用于定义一个ParameterizableViewController
,当调用时立即转发到视图。 在静态情况下,在视图生成响应之前没有Java控制器逻辑执行时使用它。
向Java中的“home”
视图转发“/”
请求的示例:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
}
而XML中使用的相同则使用<mvc:view-controller>
元素:
<mvc:view-controller path="/" view-name="home"/>
22.16.8查看解决方案
MVC配置简化了视图解析器的注册。
以下是Java配置示例,使用FreeMarker HTML模板配置内容协商视图分辨率,并将Jackson作为默认的JSON渲染视图:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.enableContentNegotiation(new MappingJackson2JsonView());
registry.jsp();
}
}
和XML一样:
<mvc:view-resolvers>
<mvc:content-negotiation>
<mvc:default-views>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
</mvc:default-views>
</mvc:content-negotiation>
<mvc:jsp/>
</mvc:view-resolvers>
不过请注意,FreeMarker,Velocity,Tiles,Groovy Markup和脚本模板也需要配置基础视图技术。
MVC命名空间提供专用元素。 例如与FreeMarker:
<mvc:view-resolvers>
<mvc:content-negotiation>
<mvc:default-views>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
</mvc:default-views>
</mvc:content-negotiation>
<mvc:freemarker cache="false"/>
</mvc:view-resolvers>
<mvc:freemarker-configurer>
<mvc:template-loader-path location="/freemarker"/>
</mvc:freemarker-configurer>
在Java配置中,只需添加相应的“Configurer”bean:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.enableContentNegotiation(new MappingJackson2JsonView());
registry.freeMarker().cache(false);
}
@Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("/WEB-INF/");
return configurer;
}
22.16.9资源服务
此选项允许遵循特定URL模式的静态资源请求由ResourceHttpRequestHandler
从资源位置列表中的任何一个提供。 这提供了一种方便的方法来从除Web应用程序根以外的位置(包括类路径上的位置)提供静态资源。 缓存期属性可用于设置远期未来的过期标头(1年是优化工具的推荐,如Page Speed和YSlow),以便客户端更有效地利用它们。 处理程序也正确地评估Last-Modified
头(如果存在),以便适当地返回304状态代码,避免客户端已经缓存的资源的不必要的开销。 例如,要从Web应用程序根目录中的public-resource
目录中提供具有/ resources / **
的URL模式的资源请求,您将使用:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/");
}
}
和XML一样:
<mvc:resources mapping="/resources/**" location="/public-resources/"/>
为了在未来1年的时间内为这些资源提供服务,以确保最大限度地利用浏览器缓存和减少浏览器发出的HTTP请求:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/").setCachePeriod(31556926);
}
}
<mvc:resources mapping="/resources/**" location="/public-resources/" cache-period="31556926"/>
有关详细信息,请参阅静态资源的HTTP缓存支持。
映射属性必须是可以由SimpleUrlHandlerMapping
使用的Ant模式,location属性必须指定一个或多个有效的资源目录位置。 可以使用逗号分隔的值列表来指定多个资源位置。 指定的位置将按照指定的顺序检查是否存在任何给定请求的资源。 例如,要启用来自Web应用程序根目录和/etc/public-web-resources /
的已知路径的资源,可以在类路径中的任何jar中使用:
@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/", "classpath:/META-INF/public-web-resources/");
}
}
<mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/public-web-resources/"/>
当部署新版本的应用程序时可能会更改的资源,建议您将版本字符串合并到用于请求资源的映射模式中,以便您可以强制客户端请求新部署的应用程序资源版本。版本化URL的支持内置于框架中,可以通过在资源处理程序中配置资源链来实现。该链由一个ResourceResolver
实例后跟一个或多个ResourceTransformer
实例组成。他们一起可以提供任意解决和资源转型。
内置的VersionResourceResolver
可以配置不同的策略。例如,FixedVersionStrategy
可以使用属性,日期或其他作为版本。 ContentVersionStrategy
使用从资源内容(称为“指纹识别”URL)计算出的MD5哈希值。请注意,在服务资源时,VersionResourceResolver
会自动将解析的版本字符串用作HTTP ETag头值。
ContentVersionStrategy
是一个很好的默认选择,除非不能使用(例如使用JavaScript模块加载器)。您可以根据不同的模式配置不同的版本策略,如下所示。请记住,计算基于内容的版本是昂贵的,因此在生产中应该启用资源链缓存。
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/public-resources/")
.resourceChain(true).addResolver(
new VersionResourceResolver().addContentVersionStrategy("/**"));
}
}
<mvc:resources mapping="/resources/**" location="/public-resources/">
<mvc:resource-chain>
<mvc:resource-cache/>
<mvc:resolvers>
<mvc:version-resolver>
<mvc:content-version-strategy patterns="/**"/>
</mvc:version-resolver>
</mvc:resolvers>
</mvc:resource-chain>
</mvc:resources>
当部署新版本的应用程序时可能会更改的资源,建议您将版本字符串合并到用于请求资源的映射模式中,以便您可以强制客户端请求新部署的应用程序资源版本。版本化URL的支持内置于框架中,可以通过在资源处理程序中配置资源链来实现。该链由一个ResourceResolver
实例后跟一个或多个ResourceTransformer
实例组成。他们一起可以提供任意解决和资源转型。
内置的VersionResourceResolver
可以配置不同的策略。例如,FixedVersionStrategy
可以使用属性,日期或其他作为版本。 ContentVersionStrategy
使用从资源内容(称为“指纹识别”URL)计算出的MD5哈希值。请注意,在服务资源时,VersionResourceResolver
会自动将解析的版本字符串用作HTTP ETag头值。
ContentVersionStrategy
是一个很好的默认选择,除非不能使用(例如使用JavaScript模块加载器)。您可以根据不同的模式配置不同的版本策略,如下所示。请记住,计算基于内容的版本是昂贵的,因此在生产中应该启用资源链缓存。
22.16.10回退在“默认”Servlet服务资源
这允许将DispatcherServlet
映射到“/”
(从而覆盖容器的默认Servlet的映射),同时仍允许静态资源请求由容器的默认Servlet处理。 它配置一个DefaultServletHttpRequestHandler
,其URL映射为“/ **”
,相对于其他URL映射为最低优先级。
该处理程序将所有请求转发到默认Servlet。 因此,重要的是它按照所有其他URL HandlerMappings的顺序保持最后。 如果您使用<mvc:annotation-driven>
或者如果您正在设置自己的Customler HandlerMapping实例,请确保将其order属性设置为低于DefaultServletHttpRequestHandler(Integer.MAX_VALUE)
的值。
要使用默认设置启用该功能,请使用:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
<mvc:default-servlet-handler/>
覆盖“/”Servlet映射的注意事项是,默认Servlet的RequestDispatcher必须通过名称而不是路径检索。 DefaultServletHttpRequestHandler将尝试在启动时自动检测容器的默认Servlet,使用大多数主要Servlet容器(包括Tomcat,Jetty,GlassFish,JBoss,Resin,WebLogic和WebSphere)的已知名称列表。 如果默认的Servlet已经使用不同的名称自定义配置,或者如果使用了默认Servlet名称未知的其他Servlet容器,则必须显式提供缺省Servlet的名称,如以下示例所示:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable("myCustomDefaultServlet");
}
}
<mvc:default-servlet-handler default-servlet-name="myCustomDefaultServlet"/>
22.16.11路径匹配
这允许自定义与URL映射和路径匹配相关的各种设置。 有关各个选项的详细信息,请查看PathMatchConfigurer API。
以下是Java配置中的一个示例:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer
.setUseSuffixPatternMatch(true)
.setUseTrailingSlashMatch(false)
.setUseRegisteredSuffixPatternMatch(true)
.setPathMatcher(antPathMatcher())
.setUrlPathHelper(urlPathHelper());
}
@Bean
public UrlPathHelper urlPathHelper() {
//...
}
@Bean
public PathMatcher antPathMatcher() {
//...
}
}
和XML一样,使用<mvc:path-matching>
元素:
<mvc:annotation-driven>
<mvc:path-matching
suffix-pattern="true"
trailing-slash="false"
registered-suffixes-only="true"
path-helper="pathHelper"
path-matcher="pathMatcher"/>
</mvc:annotation-driven>
<bean id="pathHelper" class="org.example.app.MyPathHelper"/>
<bean id="pathMatcher" class="org.example.app.MyPathMatcher"/>
22.16.12消息转换器
如果要替换由Spring MVC创建的默认转换器,或者如果您只想自定义extendMessageConverters()
或将额外的转换器添加到默认转换器),可以通过覆盖configureMessageConverters()
来实现HttpMessageConverter
的定制。
以下是使用自定义ObjectMapper
而不是默认值添加Jackson JSON和XML转换器的示例:
@Configuration
@EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
.indentOutput(true)
.dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
.modulesToInstall(new ParameterNamesModule());
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.xml().build()));
}
}
在这个例子中,Jackson2ObjectMapperBuilder
用于为MappingJackson2HttpMessageConverter
和MappingJackson2XmlHttpMessageConverter
创建一个通用配置,并启用缩进功能,自定义日期格式以及jackson-module-parameter-names
的注册,增加了访问参数名称的支持(Java 8中添加的功能)。