EnableWebMvc


1、启用MVC Java config 或 MVC XML namespace

想要启用MVC Java config,只需要将@EnableWebMvc添加到你的一个@Configuration class即可。

@Configuration
@EnableWebMvc
public class WebConfig {

}

或者在XML中,需要在你的DispatcherServlet context (或你的root context -- 如果没有定义DispatcherServlet context的话)内使用 mvc:annotation-driven 元素:

<?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 以支持使用注解Controller的注解方法(如@RequestMapping、@ExceptionHandler)来处理request。

它还启用了如下内容:

  1. Spring 3 风格的类型转换 -- 通过一个ConversionService 实例 配合JavaBean PropertyEditors,用于Data Binding。
  2. 支持@NumberFormat注解通过ConversionService 来格式化Number字段。
  3. 支持使用@DateTimeFormat注解来格式化Date、Calendar、Long、以及Joda Time字段。
  4. 支持使用@Valid校验@Controller input -- 如果classpath中存在一个JSR-303 Provider。
  5. HttpMessageConverter支持@RequestMapping或@ExceptionHandler method的 @RequestBody method parameters和@ResponseBody method 返回值。 -- 比较长,其实就是支持handler (controller)的@RequestBody参数/@ResponseBody返回值。

下面是mvc:annotation-driven 设置的完整的HttpMessageConverter列表:

  1. ByteArrayHttpMessageConverter converts byte arrays.
  2. StringHttpMessageConverter converts strings.
  3. ResourceHttpMessageConverter converts to/from org.springframework.core.io.Resource for all media types.
  4. SourceHttpMessageConverter converts to/from a javax.xml.transform.Source.
  5. FormHttpMessageConverter converts form data to/from a MultiValueMap<String, String>.
  6. Jaxb2RootElementHttpMessageConverter converts Java objects to/from XML — added if JAXB2 is present and Jackson 2 XML extension is not present on the classpath.
  7. MappingJackson2HttpMessageConverter converts to/from JSON — added if Jackson 2 is present on the classpath.
  8. MappingJackson2XmlHttpMessageConverter converts to/from XML — added if Jackson 2 XML extension is present on the classpath.
  9. AtomFeedHttpMessageConverter converts Atom feeds — added if Rome is present on the classpath.
  10. RssChannelHttpMessageConverter converts RSS feeds — added if Rome is present on the classpath.

See Section 22.16.12, “Message Converters” for more information about how to customize these default converters.

Jackson JSON 和 XML converters是使用Jackson2ObjectMapperBuilder创建的ObjectMapper实例们来创建的,其目的是提供一个更好的默认配置。

该builder定制了Jackson的默认properties:

  1. DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES is disabled.
  2. MapperFeature.DEFAULT_VIEW_INCLUSION is disabled.

它还自动注册了下列很有名的模块 -- 如果能够在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.

2、修改已提供的配置

想要以Java形式定制默认的配置,你可以简单的实现WebMvcConfigurer接口,或者继承WebMvcConfigurerAdapter并重写需要的方法

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    // Override configuration methods...

}

想要定制 mvc:annotation-driven的默认配置,需要检查其attributes和其子元素。可以查看Spring MVC XML schema,或者使用IDE的自动完成功能来探索可用的attributes和子元素。

3、类型转换和格式化

默认已注册了Number和Date类型的formatters,支持@NumberFormat和@DateTimeFormat注解。 还注册了对于Joda Time格式化库的完全支持 -- 需要在classpath中有Joda Time。想要注册自定义的formatters和converters,重写addFormatters方法即可:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // Add formatters and/or converters
    }

}

在MVC namespace中,mvc:annotation- driven会应用同样的默认设置。如果想注册自己的formatters和converters,只需要提供一个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>

See Section 9.6.4, “FormatterRegistrar SPI” and the FormattingConversionServiceFactoryBean for more information on when to use FormatterRegistrars.

4、校验

Spring提供了一个Validator接口,可被用于在应用的所有layers上的校验。在Spring MVC中,你可以将其配置成一个全局的Validator实例,用于所有@Valid或@Validated controller method argument的地方,和/或一个Controller内局部的Validator,通过一个@InitBinder方法。 全局的和局部的validator实例们可以组合校验。

Spring还支持 JSR-303/JSR-349 Bean Validation -- 通过LocalValidatorFactoryBean,LocalValidatorFactoryBean会将Spring的Validator接口适配到javax.validation.Validator。 该类可以被插入Spring MVC作为一个全局的validator,下面有讲。

使用@EnableWebMvc或 <mvc:annotation-driven>,会默认地在Spring MVC中自动注册Bean Validation支持 -- 通过LocalValidatorFactoryBean,前提是classpath中有一个Bean Validation Provider,如Hibernate Validator。

有时候,将LocalValidatorFactoryBean注入到controller或其他类中很方便。最简单的方法就是声明你自己的@Bean,并使用@Primary以避免与MVC Java config提供的那个发送冲突。

如果你更喜欢MVC Java config提供的那个,你会需要重写 WebMvcConfigurationSupport 的mvcValidator(),并声明方法显式地返回LocalValidatorFactory而非Validator。详见 Section 22.16.13, “Advanced Customizations with MVC Java Config”

或者,你可以配置自己的全局 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>

想要联合全局的和局部的validation,只需要简单地添加一个或多个local validator(s):

@Controller
public class MyController {

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.addValidators(new FooValidator());
    }

}

使用这种最小化的配置,任何时间只要@Valid或@Validated method argument遇到了,就会被配置好的validators校验。任何校验都会自动的被暴露成BindingResult中的errors,可被方法参数访问,在Spring MVC HTML views中也是可渲染的。 -- 其实都是废话,就是说 method(..@Valid T t, BindingResult br)。

5、拦截器

HandlerInterceptor 或 WebRequestInterceptor,可被用于所有incoming requests 或 特定的URL path 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/*");
    }

}

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

6、内容协商

你可以配置Spring MVC如何判断request中的请求媒体类型。可用的选项是:①检查URL路径中的文件扩展名,②检查Accept header,③特定的query parameter,④或回滚到默认的内容类型 -- 当什么都不请求时。 默认,先检查request URI中的扩展名,再检查Accept header。

MVC Java config和 MVC namespace 默认会注册 json、xml、rss、atom,前提是classpath中有相应的依赖。可以显式地注册额外的path extension-to-media类型映射。

下面是通过MVC Java config定制内容协商选项的例子:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.mediaType("json", MediaType.APPLICATION_JSON);
    }
}

中MVC namespace中,mvc:annotation-driven元素有个 content-negotiation-manager attribute,它预期的是一个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 config 或 MVC namespace,你会需要创建一个ContentNegotiationManager实例,并用它来配置RequestMappingHandlerMapping -- 用于request mapping目的,以及配置RequestMappingHandlerAdapter 和 ExceptionHandlerExceptionResolver -- 用于内容协商目的。

注意,ContentNegotiatingViewResolver 现在也可以使用一个ContentNegotiationManager来配置,所以你可以使用一个共享的实例。

更多的情况下,配置多个ContentNegotiationManager实例(可能包含定制的ContentNegotiationStrategy实现)可能很有用。例如,你可以使用一个ContentNegotiationManager来配置ExceptionHandlerExceptionResolver,然后总是将请求的媒体类型处理成”application/json”。或者,你可能想插入一个定制的策略,如果没有内容类型被请求时,使用一些逻辑来选择默认的内容类型 --如XML或JSON。

7、View Controllers

有一种简便的方式来定义一个ParameterizableViewController --当调用时会立即转发到一个view。一般用于静态案例。

例如,以Java形式将对”/”的请求转发至”home” view:

@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"/>

8、View Resolvers

MVC config 简化了view resolvers的注册。

下面是一个Java config示例,配置了内容协商视图解决方案使用FreeMarker HTML 模板,还配置了Jackson作为默认JSON View:

@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和脚本模板,同样要求配置底层的view 技术。

MVC namespace提供了专用的元素。 例如,针对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 config中,只需要简单的添加相应的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;
    }

}

9、服务于资源

该选项允许静态资源请求按照特定的URL pattern,由ResourceHttpRequestHandler来服务 -- 从Resource locations列表中的任意一个。这提供了一种便捷的方式来服务静态资源,不只是web app root的位置,还 包括classpath (牛掰大了) 。其cache-period property可以被用来设置失效headers (对于Page Speed and YSlow等工具来说,推荐设为1年),这样它们会被client更有效的使用。handler还会正确的evaluate Last-Modified header (如果有) -- 并在恰当的时候返回 304 status code,这样会避免不必要的重复资源获取。例如,想要使用 /resources/** 形式的URL pattern来请求 web root 下public-resources 文件夹中内容,你可以这样做:

@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年有效期:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/").setCachePeriod(31556926);
    }

}

XML中:

<mvc:resources mapping="/resources/**" location="/public-resources/" cache-period="31556926"/>

注意:mapping attribute 必须是一个Ant 模式的,这样才能被SimpleUrlHandlerMapping使用;location attribute 必须指定一个或多个有效的资源目录。 多个资源位置可以使用逗号间隔。指定的位置会按照指定的顺序被检查。例如,想要同时支持 web root 和classpath中任何jar中的 /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/");
    }

}

在XML中:

<mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/public-web-resources/"/>

当资源可能随着新版本的部署而发生改变时,建议在请求资源的mapping pattern中加入一个版本字符串,这样你可以强制客户端请求新部署版本的资源。 框架天然支持带版本的URLs,且可以通过配置resource handler的resource chain来启用。该chain由一个或多个ResourceResolver实例(伴有一个或多个ResourceTransformer实例)组成。它们一起可以为资源提供任意的resolution和transformation。

内置的VersionResourceResolver,可以被配置不同的策略。例如,一个FixedVersionStrategy可以使用一个property、一个date,或其他作为版本。一个ContentVersionStrategy 使用根据资源内容计算的MD5 hash(也被叫做fingerprinting URLs)作为版本。 注意,VersionResourceResolver会自动使用resolved version strings作为 HTTP ETag header的值。

ContentVersionStrategy是一个很好的默认选择,除非不能使用 (例如,当使用JavaScript 模块加载器时)。 你可以配置不同的版本策略。务必记住,基于内容计算的版本是很耗费系统资源的,因此,在生产模型中应该启用resource chain 缓存。

Java config 例子:

@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("/**"));
    }

}

XML例子:

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

为了让上面的内容正常工作,应用必须在URLs中带有版本。 最简单的方式就是配置一个ResourceUrlEncodingFilter,它封装了response,并重写了其encodeURL method。在JSPs、FreeMarker、Velocity、和任何调用response encodeURL method的其他view技术中,都可以工作。或者,一个应用也可以注入和直接使用 ResourceUrlProvider bean,MVC Java config 和 MVC namespace会自动声明它。

Webjars are also supported with WebJarsResourceResolver, which is automatically registered when the "org.webjars:webjars-locator" library is on classpath. This resolver allows the resource chain to resolve version agnostic libraries from HTTP GET requests "GET /jquery/jquery.min.js" will return resource "/jquery/1.2.0/jquery.min.js". It also works by rewriting resource URLs in templates <script src="/jquery/jquery.min.js"/> → <scriptsrc="/jquery/1.2.0/jquery.min.js"/>.

10、回滚到默认的Servlet

这允许将 DispatcherServlet 映射到 “/”(因此,覆盖了容器的default Servlet的映射),同时,仍然允许容器的default Servlet来处理静态资源请求。它配置了一个DefaultServletHttpRequestHandler,并映射到 “/**”,该映射具有最低的优先级。

该Handler 会将所有请求转发给default Servlet。因此,它在所有其他URL HandlerMappings中排名最后很重要(就是必须放在最后的意思)。当你使用mvc:annotation-driven 或 当你在设置你自己定制的HandlerMapping实例时,其order property的值应比DefaultServletHttpRequestHandler的值小,嗯嗯,后者是 Integer.MAX_VALUE

To enable the feature using the default setup use:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

}

或者,在XML中:

<mvc:default-servlet-handler/>

针对覆盖 “/” Servlet mapping的 警告 是:default Servlet 的 RequestDispatcher 必须通过name来获取,而非通过path。DefaultServletHttpRequestHandler 会在Servlet容器启动时试图自动探测其default Servlet -- 使用一个包含大多数已知Servlet容器的name列表(包括Tomcat、Jetty、GlassFish、JBoss、Resin、WebLogic、WebSphere) 。 如果default Servlet已经被配置了不同的name,或者如果使用了一个不同的Servlet容器(不知其default Servlet name),那么必须按照如下方式来提供default Servlet name:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable("myCustomDefaultServlet");
    }

}

或者在XML中:

<mvc:default-servlet-handler default-servlet-name="myCustomDefaultServlet"/>

11、Path Matching - 路径匹配

这里允许针对URL mapping和path matching 自定义不同的设置。 针对每个选项的详细信息,请检查PathMatchConfigurer API。

下面是一个Java config例子:

@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"/>

12、Message Converters - 消息转换器

针对HttpMessageConverter的定制,可以通过重写configureMessageConverters() 或重写extendMessageConverters()来实现,二者不同之处在于:前者会取代Spring MVC创建的默认转换器,后者则是添加额外的转换器。(个人推荐后者)

下面是一个取代默认设置的例子:

@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 来为MappingJackson2HttpMessageConverterMappingJackson2XmlHttpMessageConverter创建一个通用的配置,该配置:支持缩进、自定义的日期格式‘jackson- module-parameter-names的注册(会添加访问parameter names的支持 -- Java 8中的功能)。

启用Jackson XML的缩进支持,需要jaskson-dataformat-xml 还有 woodstox-core-asl 依赖。

其他有趣的可用Jackson模块包括:

  1. jackson-datatype-money: support for javax.money types (unofficial module)
  2. jackson-datatype-hibernate: support for Hibernate specific types and properties (including lazy-loading aspects)

在XML中:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper" ref="objectMapper"/>
        </bean>
        <bean class="org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter">
            <property name="objectMapper" ref="xmlMapper"/>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

<bean id="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"
      p:indentOutput="true"
      p:simpleDateFormat="yyyy-MM-dd"
      p:modulesToInstall="com.fasterxml.jackson.module.paramnames.ParameterNamesModule"/>

<bean id="xmlMapper" parent="objectMapper" p:createXmlMapper="true"/>

13、MVC Java config中的高级定制

如果你在前面的例子中看到的一样,MVC Java config 和 MVC namespace 都提供了高级别的指令 -- 不需要高深的底层知识。相反,它会帮助你将精力放在应用上面。然而,某些时候你的确可能需要更细化的控制,或者你只是简单地想理解底层的配置。

迈向更细化的控制的第一步,就是查看底层的beans。在MVC Java config 中你可以看到javadocs 和 WebMvcConfigurationSupport中的@Bean methods。 通过@EnableWebMvc注解,可以自动导入该类中的配置。 事实上,你点开@EnableWebMvc 就可以看到 @Import语句。

第二步就是自定义下WebMvcConfigurationSupport创建的某个bean的一个property,或者,干脆提供你自己的实例。这需要两件事情:移除@EnableWebMvc注解,以阻止导入,然后继承DelegatingWebMvcConfiguration -- 它是WebMvcConfigurationSupport的一个子类。 见下面的例子:

@Configuration
public class WebConfig extends DelegatingWebMvcConfiguration {

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        // ...
    }

    @Override
    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
        // Create or let "super" create the adapter
        // Then customize one of its properties
    }

}

一个应用应该只有一个继承自DelegatingWebMVCConfiguration的配置 或 单一的@EnableWebMvc class, 因为它们会注册相同的底层beans。

以这种方式修改beans,不会阻止你使用任何更高级别的方式 -- 就是前面讲述的那些方式。WebMvcConfigurerAdapter的子类们和WebMvcConfigurer实现们 仍然被使用。

14、MVC namespace中的高级定制

好吧,MVC namespace中更细化的控制有点麻烦。

建议配置一个 BeanPostProcessor,用它来侦探你想定制的bean,然后修改器properties即可。例如:

@Component
public class MyPostProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
        if (bean instanceof RequestMappingHandlerAdapter) {
            // Modify properties of the adapter
        }
    }

}

这里要注意,MyPostProcessor 需要被包含在一个 <componentscan/>中,这样能够自动被侦测到。或者,如果你喜欢,也可以将其显式地声明为一个XML bean定义。


原文链接:https://www.cnblogs.com/duanxz/p/4875153.html