https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-config.html
MVC Java配置和MVC XML命名空间提供了适用于大多数应用程序的默认配置,以及用于自定义它的配置API。
启用MVC配置
在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等的有效负载转换器)。
MVC配置API
在Java配置中,可以实现WebMvcConfigurer接口,如下例所示:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
// Implement configuration methods...
}
在XML中,你可以检查<mvc:annotation-driven/>
的属性和子元素。可以查看Spring MVC的XML模式或使用IDE的代码补全功能来发现可用的属性和子元素。
类型转换
默认情况下,会安装各种数字和日期类型的格式化程序,并通过@NumberFormat和@DateTimeFormat对字段进行自定义支持。
在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>
默认情况下,Spring MVC在解析和格式化日期值时会考虑请求的Locale。这对于将日期表示为带有“input”表单字段的字符串的表单有效。然而,对于“date”和“time”表单字段,浏览器使用HTML规范中定义的固定格式。对于这些情况,可以如下自定义日期和时间格式:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(registry);
}
}
验证
默认情况下,如果类路径上存在Bean Validation(例如Hibernate Validator),则LocalValidatorFactoryBean将注册为全局验证器,以便与控制器方法参数上的@Valid和@Validated一起使用。
在Java配置中,可以自定义全局验证器实例,如下例所示:
@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,请创建一个带有@Primary标记的bean,以避免与MVC配置中声明的bean发生冲突。
拦截器
在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/**");
}
}
以下示例展示了如何在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:interceptors>
拦截器并不是理想的安全层,因为它们可能会与带注解的控制器路径匹配不匹配,这也可能透明地匹配尾部斜杠和路径扩展名以及其他路径匹配选项。许多这些选项已被弃用,但不匹配的可能性仍然存在。通常,建议使用Spring Security,它包括一个专门的MvcRequestMatcher以与Spring MVC路径匹配对齐,并且还具有一个安全防火墙,可以阻止URL路径中的许多不需要的字符。
XML配置将拦截器声明为MappedInterceptor bean,这些bean随后被任何HandlerMapping bean(包括来自其他框架的bean)检测到。相比之下,Java配置仅将其管理的HandlerMapping bean传递拦截器。要在Spring MVC和其他框架的HandlerMapping bean之间重用相同的拦截器,请声明MappedInterceptor bean(不要在Java配置中手动添加它们),或者在Java配置和其他HandlerMapping bean中都配置相同的拦截器。
内容类型
可以配置Spring MVC如何从请求中确定请求的媒体类型(例如Accept头、URL路径扩展名、查询参数等)。
默认情况下,只检查Accept头。
如果必须使用基于URL的内容类型解析,请考虑使用查询参数策略而不是路径扩展。
在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>
消息转换器
可以通过覆盖configureMessageConverters()在Java配置中设置要使用的HttpMessageConverter实例,替换默认使用的实例。还可以通过覆盖extendMessageConverters()在最后自定义已配置的消息转换器列表。
在Spring Boot应用程序中,WebMvcAutoConfiguration会添加它检测到的任何HttpMessageConverter bean,以及默认的转换器。因此,在Boot应用程序中,最好使用HttpMessageConverters机制。或者,也可以使用extendMessageConverters在最后修改消息转换器。
以下示例添加了XML和Jackson JSON转换器,并使用自定义的ObjectMapper替换了默认的转换器:
@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-joda:支持Joda-Time类型。
- jackson-datatype-jsr310:支持Java 8日期和时间API类型。
- jackson-datatype-jdk8:支持其他Java 8类型,如Optional。
- jackson-module-kotlin:支持Kotlin类和数据类。
要在Jackson XML支持中启用缩进,除了需要jackson-dataformat-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"/>
视图控制器
这是定义一个在调用时立即转发到视图的ParameterizableViewController的快捷方式。在静态情况下,当视图生成响应之前没有Java控制器逻辑要运行时,可以使用它。
以下Java配置示例将请求 / 转发到一个名为home的视图:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
}
以下示例使用XML,通过mvc:view-controller元素实现了与前一个示例相同的功能:
<mvc:view-controller path="/" view-name="home"/>
如果一个@RequestMapping方法被映射到任何HTTP方法的URL,那么不能使用视图控制器来处理相同的URL。这是因为通过URL匹配到注解控制器被认为是足够强烈的端点所有权指示,因此可以向客户端发送405(METHOD_NOT_ALLOWED)、415(UNSUPPORTED_MEDIA_TYPE)或类似的响应以帮助调试。因此,建议避免在注解控制器和视图控制器之间分割URL处理。
视图解析器
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、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 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;
}
}
静态资源
这个选项提供了一种方便的方式来从基于资源的列表中提供静态资源。
在下一个示例中,给定一个以/resources开头的请求,使用相对路径来查找并提供静态资源,这些资源位于Web应用程序根目录下的/public或类路径下的/static。资源被提供时带有一年的未来过期时间,以确保最大程度地使用浏览器缓存并减少浏览器发出的HTTP请求。从Resource#lastModified推断出最后修改信息,以便支持带有"Last-Modified"头的HTTP条件请求。
以下清单展示了如何使用Java配置来实现这一点:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/public", "classpath:/static/")
.setCacheControl(CacheControl.maxAge(Duration.ofDays(365)));
}
}
以下示例展示了如何在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,以便它可以被注入到其他bean中。还可以使用ResourceUrlEncodingFilter使重写对Thymeleaf、JSPs、FreeMarker等使用URL标签的依赖HttpServletResponse#encodeURL的视图技术透明化。
请注意,当同时使用EncodedResourceResolver(例如,用于提供gzip压缩或brotli编码资源)和VersionResourceResolver时,必须按此顺序注册它们。这确保了基于未编码文件始终可靠地计算内容版本。
对于WebJars,像/webjars/jquery/1.2.0/jquery.min.js这样的带版本URL是推荐和最有效的使用方式。相关的资源位置在Spring Boot中已经配置好了(或可以通过ResourceHandlerRegistry手动配置),不需要添加org.webjars:webjars-locator-core依赖项。
无版本的URL,如/webjars/jquery/jquery.min.js,通过WebJarsResourceResolver得到支持,当org.webjars:webjars-locator-core库存在于类路径上时,该解析器会自动注册,但这会以扫描类路径为代价,可能会减慢应用程序启动速度。解析器可以重写URL以包含jar的版本,并且还可以匹配不带版本的传入URL——例如,从/webjars/jquery/jquery.min.js到/webjars/jquery/1.2.0/jquery.min.js。
基于ResourceHandlerRegistry的Java配置提供了更细致的控制选项,例如最后修改行为和优化的资源解析。
默认Servlet
Spring MVC允许将DispatcherServlet映射到 /
(从而覆盖容器默认Servlet的映射),同时仍然允许静态资源请求由容器的默认Servlet处理。它配置了一个DefaultServletHttpRequestHandler,URL映射为 /**,并且相对于其他URL映射具有最低优先级。
这个处理器将所有请求转发给默认的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,使用大多数主要Servlet容器(包括Tomcat、Jetty、GlassFish、JBoss、Resin、WebLogic和WebSphere)的已知名称列表。如果默认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"/>
路径匹配
可以自定义与路径匹配和URL处理相关的选项。
以下示例展示了如何在Java配置中自定义路径匹配:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class));
}
private PathPatternParser patternParser() {
// ...
}
}
以下示例展示了如何在XML配置中自定义路径匹配:
<mvc:annotation-driven>
<mvc:path-matching
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"/>
高级Java配置
@EnableWebMvc导入了DelegatingWebMvcConfiguration,它:
- 为Spring MVC应用程序提供默认的Spring配置
- 检测并委托给WebMvcConfigurer实现以自定义该配置。
对于高级模式,可以移除@EnableWebMvc并直接从DelegatingWebMvcConfiguration继承,而不是实现WebMvcConfigurer,如下例所示:
@Configuration
public class WebConfig extends DelegatingWebMvcConfiguration {
// ...
}
可以保留WebConfig中的现有方法,但现在也可以覆盖基类中的bean声明,并且仍然可以在类路径上拥有任意数量的其他WebMvcConfigurer实现。
高级XML配置
MVC命名空间没有高级模式。如果需要自定义无法以其他方式更改的bean属性,可以使用Spring ApplicationContext的BeanPostProcessor生命周期钩子,如下例所示:
@Component
public class MyPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
// ...
}
}
需要将MyPostProcessor声明为bean,可以在XML中显式声明,或者通过声明来检测。