在之前的Blog – Spring MVC 之 一些特殊的Bean中的”WebApplicationContext中一些特殊的Bean”和”默认的DispatcherServlet配置”中已经解释了Spring MVC中的一些特殊的Bean以及DispatcherServlet中的使用的一些默认实现。在这个部分中你将学到配置Spring MVC的2种额外的方法。也就是MVC Java配置和MVC XML命名空间。
MVC Java配置和MVC会命名空间提供类似的配置用来重写DispatcherServlet的默认设置.它的目标是为大多数应用分出那些不得不创建的同样的配置并且为配置Spring MVC提供较高层次的构念.使得Spring MVC可以服务于作为一个简单的起点,需要很少或根本没有先验知识的基本配置。遵循之前所说的COC(Convention over configuration) – Spring MVC 之 COC support
你可以按照你的偏爱选择使用MVC Java配置或者MVC命名空间。你也可以看下面的特性,看下面的配置MVC Java配置显得更加容易。并且获得更加细粒度的自定义创建Spring MVC的beans.但是让我们重新开始吧。
在你的一个标注了@Configuration类上添加注解@EnableWebMvc使得你能够MVC Java配置。
@Configuration
@EnableWebMvc
public class WebConfig {
}
在你的DispatcherServlet上下文(或者没如果你有定义DispatcherServlet上下文也可以在你的root上下文中)的XML文件中使用mvc:annotation-driven也能类似相同的效果。
<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(尤其)为了支持处理带有注解的的Controller使用了@RequestMapping,@ExceptionHandler或者其它注解的请求。
以下特性也可以:
下面是mvc:annotation-driven标签设置的HttpMessageConverters的完整列表:
如果你想了解自定义这些默认的转换器,可以查看后面的 – 12、Message Converters
注意:Jackson JSON和XML转换器是通过ObjectMapper实例Jackson2ObjectMapperBuilder来创建的。主要是为了提供一个更好的配置.
这个builder使用下面的来自定义了Jackson的配置属性
1. DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES is disabled.
2. MapperFeature.DEFAULT_VIEW_INCLUSION is disabled.
它会自动注册下面众所周知的modules,如果它们在classpath被检测到的话。
1 jackson-datatype-jdk7: support for Java 7 types like java.nio.file.Path.
2. jackson-datatype-joda: support for Joda-Time types.
3. jackson-datatype-jsr310: support for Java 8 Date & Time API types.
4. jackson-datatype-jdk8: support for other Java 8 types like Optional.
只需要简单的实现WebMvcConfigurer接口或者,可能去继承WebMvcConfigurerAdapter并且重写你需要的方法,你可以自定义Java中默认的配置。
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
// Override configuration methods...
}
或者通过在XML中的元素来检查一下它支持哪些子元素然后进行自定义。你可以查看Spring MVC XML schema或者使用你的IDE代码completion特性来查看哪些元素和子元素是有效的。
默认情况下,Spring中有对于Number和Data类型的格式化,以及支持@NumberFormat和@DateTimeFormat注解。如果在classpath中包含Joda-Time的库,Spring会完全支持Joda Time的格式化库。如果想要注册自定义的formatter和converters,重写addFormatters方法:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addFormatters(FormatterRegistry registry) {
// Add formatters and/or converters
}
}
在MVC命名空间中也添加同样的默认应用.注册自定义formatters和converters仅仅需要提供一个ConversionService:
<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>
注意:
See Spring 8.6.4, “FormatterRegistrar SPI”和FormattingConversionServiceFactoryBean可以查看如何使用FormatterRegistrars.
Spring提供Validator interface用来验证应用的所有层。在Spring MVC中你可以通过使用一个全局的Validator来配置它。不管是在Controller方法的参数中使用@Valid或者@Validated都会生效,并且可以使用@InitBinder方法来给Controller提供一个本地的Validator.全局和本地验证器实例可以联合起来提供复杂验证。
Spring同样支持supports JSR-303/JSR-349通过LocalValidatorFactoryBean来进行Bean验证。它是Spring中的org.springframework.validation.Validator接口实现用于适应javax.validation.Validator约定的Bean Validation.后面将会描述这个类能够作为全局验证器插入Spring MVC。
默认情况下,当Bean Validation提供者在classpath中被检测到的话(比如说Hibernate Validator),Spring MVC使用@EnableWebMvc或者会通过LocalValidatorFactoryBean自动注册Bean Validation支持。
注意:有时候注入一个LocalValidatorFactoryBean到一个Controller或者另外的class中会很方便。这是一个最简单的方法来检测你自己的@Bean并且标注它为@Primary为了避免MVC Java配置提供的。如果你更喜欢使用MVC Java配置中的Validator,你需要重写WebMvcConfigurationSupport类中的mvcValidator方法并且明确的声明这个方法返回LocalValidatorFactory而不是Validator。可以看后面的章节 – 13、Advanced Customizations with MVC Java Config
了解如果继承提供的配置。
你可以配置你自己的全局Validator实例:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public Validator getValidator(); {
// return "global" validator
}
}
and in XML:
<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>
结合使用全局与本地validation,仅仅需要添加一个或者多个本地validator(s):
@Controller
public class MyController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new FooValidator());
}
}
有时候遇到的最小配置是在方法参数中使用@Valid或者@Validated,它将会被配置的validators验证。一些验证的结果为作为errors自动的暴露在BindingResult作为Controller方法中的方法参数并且同样可以渲染到Spring MVC HTML页面中。
你可以配置HandlerInterceptors或者WebRequestInterceptors应用于将要到来的请求或者一些特殊的URL路径patterns。
下面是一个通过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/*");
}
}
And in XML use the element:
<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>
你可以配置Spring MVC接收request中请求的media type。通过文件的扩展可获取选择是通过检测URL的path,检查HTTP header中的”Accept”,一个特殊的请求参数或者当什么也没有请求的时候遇到一个默认的content type.默认时候下,request URI中的路径扩展首先被checked然后才是请求头”Accept”.
默认情况下MVC Java配置和MVC命名空间注册json,xml,rss,atom。额外的路径扩展media type映射可以被明确的注册。并且同样可以有白名单作为安全的扩展作为检测RFD攻击检测.(可以看之前的Blog – Spring MVC 之 实现Controller 中的“Suffix Pattern Matching and RFD”了解更多细节).
下面是通过MVC Java配置的一个自定义content negotiation选项的例子:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.mediaType("json", MediaType.APPLICATION_JSON);
}
}
在MVC命名空间中,元素有一个content-negotiation-manager属性,可以认为是一个被ContentNegotiationManagerFactoryBean创建的ContentNegotiationManager.
<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的request mapping purposes并且RequestMappingHandlerAdapter和ExceptionHandlerExceptionResolver的content negotiation purposes.
注意:ContentNegotiatingViewResolver现在同样可以配置一个ContentNegotiationManager,因此在Spring MVC自始至终你可以使用一个共享的实例.
在更高级的情况下,通过实现自定义的ContentNegotiationStrategy反过来可能对配置多个ContentNegotiationManager实例有用.例如你想要解析请求的media类型为”application/json”可以配置ExceptionHandlerExceptionResolver带有一个ContentNegotiationManager。或者如果没有content类型被请求的时候,你可能想要插入一个自定义策略用来选择一个默认的content类型(e.g. XML或者JSON)。
这是定义一个ParameterizableViewController的捷径,当被调用的时候马上转发到一个页面.在静态的情况下,当没有java控制器逻辑视图产生的响应执行之前用它。
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
}
同样可以在在XML使用元素:
<mvc:view-controller path="/" view-name="home"/>
MVC配置注册简化页面解析器.
下面是一个Java配置例子,配置一个使用FreeMarker HTML模板进行内容转化到页面解析并且对于JSON渲染Jackson作为一个默认的View。
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.enableContentNegotiation(new MappingJackson2JsonView());
registry.jsp();
}
}
And the same in 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 和script模板同样需要配置下面的页面技术.
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;
}
}
request跟随的在详细的URI模式中选择允许的静态资源请求通过来自一个Resource列表的ResourceHttpRequestHandler服务的。它提供了相比于web应用的root一方便的方法来服务来自locations的静态资源,包括classpath路径下的。其中cache-period属性可以被用来设置长远的过期的头.handler同样会适当的评估Last-Modified头,并且适当的会返回状态码304.避免不必要的访问已经被缓存在客户端的静态资源.例如,server在public-resources目录下并且带有/resources/**模式的资源请求。
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/");
}
}
And the same in 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);
}
}
And in XML:
<mvc:resources mapping="/resources/**" location="/public-resources/" cache-period="31556926"/>
SimpleUrlHandlerMapping使用mapping属性必须是一个Ant模式,并且location属性必须指定一个或者多个有效的目录路径。多目录可以指定通过逗号分隔开.来自于给予的request的静态资源将会这个指定的位置将会按照规定的顺序被检测。例如,为了server来自于web应用和一个知道的path在其它jar中的classpath下的/META-INF/public-web-resources/的资源文件:
@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/", "classpath:/META-INF/public-web-resources/");
}
}
And in XML:
<mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/public-web-resources/"/>
未完待续…