1.WebMvcConfigurationSupport
该类提供了主要的 MVC 配置方法,通过直接继承 WebMvcConfiguration ,并在继承类上 加上 @EnableWebMvc 和 @Configuration 注解之后。便可以在子类中实现父类的方法 ,更甚至可以实现含有@Bean 注解的方法,但记得一定要在实现的方法上加上 @Bean
WebMvcConfigurationSupport 中提供了一些内容为空的方法,这些方法主要是用来给子类自定义时实现,根据方法名称的分类,有以下几种类型
1. addXxx()
为 Xxx 类型的处理器添加用户定义的处理器
2. configureXxx()
重新配置 Xxx 类型的处理器
3.extendXxx()
扩展 Xxx 类型的处理器,该方法一般放在 addXxx 和 ConfigureXxx 后面
4.getXxx()
将 Xxx 处理器类完全由子类控制
@Configuration public class WebConfiguration extends WebMvcConfigurationSupport { @Autowired private AuthorizationInterceptor authorizationInterceptor; @Autowired private SortHandlerMethodArgumentResolver sortHandlerMethodArgumentResolver; @Autowired private PageableHandlerMethodArgumentResolver pageableHandlerMethodArgumentResolver; @Autowired private StringHttpMessageConverter stringHttpMessageConverter; @Autowired private MappingJackson2HttpMessageConverter httpMessageConverter; / 拦截器配置 * @param registry 注册类 */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authorizationInterceptor) .addPathPatterns(WebConstants.API_PREFIX + “/“) .addPathPatterns(WebConstants.WEB_PREFIX + “/“); } / 静态资源 swagger + 前端 @param registry / @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(“swagger-ui.html”,”index.html”).addResourceLocations(“classpath:/META-INF/resources/“); registry.addResourceHandler(“doc.html”).addResourceLocations(“classpath:/META-INF/resources/“); registry.addResourceHandler(“/webjars/“).addResourceLocations(“classpath:/META-INF/resources/webjars/“); registry.addResourceHandler(“/static/“).addResourceLocations(“classpath:/META-INF/resources/static/“); } @Override protected void addArgumentResolvers(List argumentResolvers.add(pageableHandlerMethodArgumentResolver); argumentResolvers.add(sortHandlerMethodArgumentResolver); } / 添加转换器 / @Override public void extendMessageConverters(List if (converters.get(i) instanceof StringHttpMessageConverter){ converters.set(i, stringHttpMessageConverter); } if (converters.get(i) instanceof** MappingJackson2HttpMessageConverter) { converters.set(i, httpMessageConverter); } } } } |
---|
2.WebMvcConfigurer
该类是通过 “回调” 的方式来进行自定义化 Spring MVC 相关配置,大多数情况下,我们通过实现它的 抽象类 WebMvcConfigurerAdapter 来配置,因为它有WebMvcConfigurer 接口所有方法的一个空的实现,WebMvcConfigurer 接口中的所有方法都是 WebMvcConfigurationSupport 中提供给子类实现的空方法。
为什么是说它是通过回调来实现的自定义配置? 用DelegatingWebMvcConfiguration 和 WebMvcConfigurerComposite 类来解释。
DelegatingWebMvcConfiguration 类 简称【代理类】通过继承 WebMvcConfigurationSupport,所以拥有了 customize 自定义mvc 配置的权利。该类通过持有的对象 WebMvcConfigurerComposite 简称【聚合类】则是一个聚合了所有实现了 WebMvcConfigurer 接口的子类,如此一来,【代理类】所有实现 WebMvcConfigurationSupport 的方法调用时,则是将【聚合类】中的所有WebMvcConfiguer 接口的实现类的该方法依次进行调用
class WebMvcConfigurerComposite implements WebMvcConfigurer { // 持有应用程序中所有实现 WebMvcConfigurer 接口的子类的一个集合 private final List // public void addWebMvcConfigurers(List if (!CollectionUtils.isEmpty(configurers)) { this.delegates.addAll(configurers); } } // 被父类调用的回调方法,来自 customize 各个子类的配置 @Override public void configurePathMatch(PathMatchConfigurer configurer) { for (WebMvcConfigurer delegate : this.delegates) { delegate.configurePathMatch(configurer); } } …… |
---|
3.WebMvcConfigurerAdapter
WebMvcConfigurerAdapter是WebMvcConfigurerAdapter的子类,内部提供扩展的方法有很多,继承它可以通过重写钩子方法来实现自定义配置
详细类说明:
https://www.yuque.com/docs/share/70d380ea-b699-4bd8-8cdf-83936b970535?#
4.踩坑指南
WebMvcConfigurationSupport 在整个应用程序中只会生效一个,如果用户已经实现了 WebMvcConfigurationSupport,则 DelegatingWebMvcConfiguration 将不会生效,换句话来说,WebMvcConfigurer 的所有实现类将不会生效。而在Spring 中,如果类路径上不存在 WebMvcConfigurationSupport 的实例,则将会默认实现WebMvcConfigurerAdapter、DelegatingWebMvcConfiguration 来自定义mvc 配置。
@Configuration @ConditionalOnWebApplication @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurerAdapter.class }) // 类路径 不存在WebMvcConfigurationSupport实例才加载 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration { //…… } // 实现 adapter @Configuration @Import(EnableWebMvcConfiguration.class) @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }) public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter { private static final Log logger = LogFactory .getLog(WebMvcConfigurerAdapter.class); private final ResourceProperties resourceProperties; private final WebMvcProperties mvcProperties; private final ListableBeanFactory beanFactory; private final HttpMessageConverters messageConverters; final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer; public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, HttpMessageConverters messageConverters, ObjectProvider this.resourceProperties = resourceProperties; this.mvcProperties = mvcProperties; this.beanFactory = beanFactory; this.messageConverters = messageConverters; this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider .getIfAvailable(); } //…… } //实现代理类,进行自定义化 @Configuration public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration { private final WebMvcProperties mvcProperties; private final ListableBeanFactory beanFactory; private final WebMvcRegistrations mvcRegistrations; public EnableWebMvcConfiguration( ObjectProvider ObjectProvider ListableBeanFactory beanFactory) { this.mvcProperties = mvcPropertiesProvider.getIfAvailable(); this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique(); this.beanFactory = beanFactory; } //…… } |
---|
而当 WebMvcAutoConfiguration 不生效时会导致以下几个问题:1.WebMvcProperties 和 ResourceProperties 失效
因为两个配置类中的属性都在 WebMvcAutoConfiguration 中使用
2.类路径上的 HttpMessageConverter 失效
如:StringHttpMessageConverterConfiguration、MappingJackson2HttpMessageConverter ,因为 HttpMessageConverters 中持有着所有HttpMessageConverter的实例, 在 WebMvcAutoConfigurationAdapter 中会注入 HttpMessageConverters ,因此当 WebMvcAutoConfigurationAdapter 不加载时,则会失效,间接的造成 spring.http.encoding.charset 与 spring.jackson.date-format 假象的失效。
如:StringHttpMessageConverter 会使用 spring.http.encoding.charset 配置, 默认编码为:ISO-8859-1
@Bean @ConditionalOnMissingBean public StringHttpMessageConverter stringHttpMessageConverter() { StringHttpMessageConverter converter = new StringHttpMessageConverter( this.encodingProperties.getCharset()); converter.setWriteAcceptCharset(false); return converter; } |
---|
常用解决方案:
因为已知的配置类都已通过 @Bean 注册在容器中,那么我们可以在使用 WebMvcConfigurationSupport 时,通过 @Autowired 注入到配置类中,然后替换调 WebMvcConfigurationSupport 默认的配置即可,如:
@Configuration public class WebConfiguration extends WebMvcConfigurationSupport { @Autowired private StringHttpMessageConverter stringHttpMessageConverter; @Autowired private MappingJackson2HttpMessageConverter httpMessageConverter; / 添加转换器 / @Override public void extendMessageConverters(List if (converters.get(i) instanceof StringHttpMessageConverter){ converters.set(i, stringHttpMessageConverter); } if (converters.get(i) instanceof** MappingJackson2HttpMessageConverter) { converters.set(i, httpMessageConverter); } } } } |
---|