1.10 MVC配置

MVC Java配置和MVC XML名称空间提供适用于大多数应用程序的默认配置以及用于自定义它的配置API。

有关配置API中不可用的更多高级定制,请参阅Advanced Java Config和Advanced XML Config。

您不需要了解由MVC Java配置和MVC名称空间创建的基础bean。 如果要了解更多信息,请参阅特殊Bean类型和Web MVC配置。

1.10.1 开启MVC Configuration

在Java配置中,可以使用@EnableWebMvc批注启用MVC配置,如以下示例所示:

@Configuration
@EnableWebMvc
public class WebConfig {
}

在XML配置中,可以使用<mvc:annotation-driven>元素来启用MVC配置,如以下示例所示:

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:annotation-driven/>

</beans>

前面的示例注册了许多Spring MVC基础结构Bean,并适应了类路径上可用的依赖项(例如,JSON,XML等的有效负载转换器)。

1.10.2 MVC Config API

在Java配置中,可以实现WebMvcConfigurer接口,如以下示例所示:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    // Implement configuration methods...
}

在XML中,您可以检查<mvc:annotation-driven />的属性和子元素。 您可以查看Spring MVC XML模式或使用IDE的代码完成功能来发现可用的属性和子元素。

1.10.3 Type Conversion

默认情况下,将安装Number和Date类型的格式化程序,包括对@NumberFormat和@DateTimeFormat批注的支持。 如果类路径中存在Joda-Time,则还将安装对Joda-Time格式库的完全支持。

在Java配置中,您可以注册自定义格式器和转换器,如以下示例所示:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ...
    }
}

以下示例显示了如何在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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        https://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>

有关何时使用FormatterRegistrar实现的更多信息,请参见FormatterRegistrar SPI和FormattingConversionServiceFactoryBean。

1.10.4 Validation

默认情况下,如果Bean验证存在于类路径中(例如,Hibernate Validator),则LocalValidatorFactoryBean将注册为全局验证器,以与@Valid一起使用,并在控制器方法参数上进行Validated。

在Java配置中,您可以自定义全局Validator实例,如以下示例所示:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public Validator getValidator(); {
        // ...
    }
}

以下示例显示了如何在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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:annotation-driven validator="globalValidator"/>

</beans>

请注意,您还可以在本地注册Validator实现,如以下示例所示:

@Controller
public class MyController {

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

}

如果您需要在某个地方注入LocalValidatorFactoryBean,请创建一个bean并用@Primary标记它,以避免与MVC配置中声明的那个冲突。

1.10.5 Interceptors

在Java配置中,您可以注册拦截器以应用于传入的请求,如以下示例所示:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LocaleChangeInterceptor());
        registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
        registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
    }
}

以下示例显示了如何在XML中实现相同的配置:

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

1.10.6 Content Types

您可以配置Spring MVC如何根据请求确定请求的媒体类型(例如,Accept标头,URL路径扩展,查询参数等)。

默认情况下,首先检查URL路径扩展名-将json,xml,rss和atom注册为已知扩展名(取决于类路径依赖项)。 接受标头是第二次检查。

考虑将这些默认值更改为“仅接受”标头,并且,如果必须使用基于URL的内容类型解析,请考虑对路径扩展使用查询参数策略。 有关更多详细信息,请参见后缀匹配和后缀匹配以及RFD。

在Java配置中,您可以自定义请求的内容类型解析,如以下示例所示:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

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

以下示例显示了如何在XML中实现相同的配置:

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

1.10.7 Message Converters

您可以通过覆盖configureMessageConverters()(以替换Spring MVC创建的默认转换器)或覆盖extendMessageConverters()(以自定义默认转换器或向默认转换器添加其他转换器),以Java配置自定义HttpMessageConverter。

以下示例使用自定义的ObjectMapper代替默认的添加了XML和Jackson JSON转换器:

@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {

    @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.createXmlMapper(true).build()));
    }
}

在前面的示例中,使用Jackson2ObjectMapperBuilder为启用了缩进的MappingJackson2HttpMessageConverter和MappingJackson2XmlHttpMessageConverter创建通用配置,自定义日期格式以及jackson-module-parameter-names的注册,从而增加了对访问参数名称的支持(增加了一项功能)在Java 8中)。

该构建器自定义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日期和时间API类型。

  • jackson-datatype-jdk8:支持其他Java 8类型,例如Optional。

除了jackson-dataformat-xml之外,使用Jackson XML支持启用缩进还需要woodstox-core-asl依赖性。

其他有趣的Jackson模块也可用:

  • jackson-datatype-money:支持javax.money类型(非官方模块)。

  • jackson-datatype-hibernate:支持特定于Hibernate的类型和属性(包括延迟加载方面)。

以下示例显示了如何在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"/>

1.10.8 View Controllers

这是定义ParameterizableViewController的快捷方式,该参数可在调用时立即转发到视图。 在视图生成响应之前没有Java控制器逻辑要执行的静态情况下,可以使用它。

以下Java配置示例将对/的请求转发到名为home的视图:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("home");
    }
}

以下示例通过使用<mvc:view-controller>元素,实现了与上一示例相同的操作,但使用XML:

<mvc:view-controller path="/" view-name="home"/>

1.10.9 View Resolvers

MVC配置简化了视图解析器的注册。

以下Java配置示例通过使用JSP和Jackson作为JSON呈现的默认视图来配置内容协商视图解析:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @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,Tiles,Groovy标记和脚本模板也需要配置基础视图技术。

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 implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.enableContentNegotiation(new MappingJackson2JsonView());
        registry.freeMarker().cache(false);
    }

    @Bean
    public FreeMarkerConfigurer freeMarkerConfigurer() {
        FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
        configurer.setTemplateLoaderPath("/freemarker");
        return configurer;
    }
}

1.10.10 Static Resources

此选项提供了一种方便的方法来从基于资源的位置列表中提供静态资源。

在下一个示例中,给定一个以/ resources开头的请求,相对路径用于在Web应用程序根目录下或/ static下的类路径下相对于/ public查找和提供静态资源。 这些资源的使用期限为一年,以确保最大程度地利用浏览器缓存并减少浏览器发出的HTTP请求。 还评估Last-Modified头,如果存在,则返回304状态代码。

以下清单显示了如何使用Java配置进行操作:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

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

以下示例显示了如何在XML中实现相同的配置:

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

资源处理程序还支持ResourceResolver实现和ResourceTransformer实现的链,您可以使用它们创建用于处理优化资源的工具链。

您可以将VersionResourceResolver用于基于资源,固定应用程序版本或其他内容计算出的MD5哈希的版本化资源URL。 ContentVersionStrategy(MD5哈希)是一个不错的选择-带有一些值得注意的例外,例如与模块加载器一起使用的JavaScript资源。

以下示例显示如何在Java配置中使用VersionResourceResolver:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("/public/")
                .resourceChain(true)
                .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
    }
}

以下示例显示了如何在XML中实现相同的配置:

<mvc:resources mapping="/resources/**" location="/public/">
    <mvc:resource-chain resource-cache="true">
        <mvc:resolvers>
            <mvc:version-resolver>
                <mvc:content-version-strategy patterns="/**"/>
            </mvc:version-resolver>
        </mvc:resolvers>
    </mvc:resource-chain>
</mvc:resources>

然后,您可以使用ResourceUrlProvider重写URL,并应用完整的解析器和转换器链-例如插入版本。 MVC配置提供了ResourceUrlProvider bean,以便可以将其注入其他对象。您也可以使用ResourceUrlEncodingFilter对Thymeleaf,JSP,FreeMarker以及其他依赖于HttpServletResponse#encodeURL的URL标签的重写透明化。

请注意,在同时使用EncodedResourceResolver(例如,用于服务压缩或brotli编码的资源)和VersionResourceResolver时,必须按此顺序注册。这样可以确保始终基于未编码文件可靠地计算基于内容的版本。

WebJars还通过WebJarsResourceResolver支持,当org.webjars:webjars-locator-core库存在于类路径中时,WebJars将自动注册。解析程序可以重写URL以包括jar的版本,并且还可以与没有版本的传入URL进行匹配,例如,从/jquery/jquery.min.js到/jquery/1.2.0/jquery.min.js。

1.10.11 Default Servlet

Spring MVC允许将DispatcherServlet映射到/(从而覆盖了容器默认Servlet的映射),同时仍允许容器默认Servlet处理静态资源请求。 它使用/ **的URL映射和相对于其他URL映射的最低优先级配置DefaultServletHttpRequestHandler。

该处理程序将所有请求转发到默认Servlet。 因此,它必须按所有其他URL HandlerMappings的顺序保留在最后。 如果使用<mvc:annotation-driven>,就是这种情况。 另外,如果您设置自己的自定义HandlerMapping实例,请确保将其order属性设置为小于DefaultServletHttpRequestHandler的Integer.MAX_VALUE的值。

下面的示例说明如何使用默认设置启用功能:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

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

以下示例显示了如何在XML中实现相同的配置:

<mvc:default-servlet-handler/>

覆盖/ Servlet映射的警告是,必须通过名称而不是通过路径来检索默认Servlet的RequestDispatcher。 DefaultServletHttpRequestHandler尝试使用大多数主要Servlet容器(包括Tomcat,Jetty,GlassFish,JBoss,Resin,WebLogic和WebSphere)的已知名称列表,在启动时自动检测容器的默认Servlet。 如果默认Servlet是使用其他名称自定义配置的,或者在默认Servlet名称未知的情况下使用了不同的Servlet容器,那么您必须明确提供默认Servlet的名称,如以下示例所示:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

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

}

以下示例显示了如何在XML中实现相同的配置:

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

1.10.12 Path Matching

您可以自定义与URL的路径匹配和处理有关的选项。 有关各个选项的详细信息,请参见PathMatchConfigurer javadoc。

以下示例显示了如何在Java配置中自定义路径匹配:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer
            .setUseSuffixPatternMatch(true)
            .setUseTrailingSlashMatch(false)
            .setUseRegisteredSuffixPatternMatch(true)
            .setPathMatcher(antPathMatcher())
            .setUrlPathHelper(urlPathHelper())
            .addPathPrefix("/api",
                    HandlerTypePredicate.forAnnotation(RestController.class));
    }

    @Bean
    public UrlPathHelper urlPathHelper() {
        //...
    }

    @Bean
    public PathMatcher antPathMatcher() {
        //...
    }

}

以下示例显示了如何在XML中实现相同的配置:

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

1.10.13 Advanced Java Config

@EnableWebMvc导入DelegatingWebMvcConfiguration,其中:

  • 为Spring MVC应用程序提供默认的Spring配置

  • 检测并委托给WebMvcConfigurer实现以自定义该配置。

对于高级模式,可以删除@EnableWebMvc并直接从DelegatingWebMvcConfiguration扩展而不是实现WebMvcConfigurer,如以下示例所示:

@Configuration
public class WebConfig extends DelegatingWebMvcConfiguration {

    // ...

}

您可以将现有方法保留在WebConfig中,但是现在您还可以覆盖基类中的bean声明,并且在类路径上仍然可以具有任意数量的其他WebMvcConfigurer实现。

1.10.14 Advanced XML Config

MVC命名空间没有高级模式。 如果您需要在bean上自定义一个不能更改的属性,则可以使用Spring ApplicationContext的BeanPostProcessor生命周期挂钩,如以下示例所示:

@Component
public class MyPostProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
        // ...
    }
}

请注意,您需要将MyPostProcessor声明为bean,可以用XML显式声明,也可以通过<component-scan />声明将其检测出来。

最后更新于