springboot 2.0配置文件不生效原因

/ 默认分类Java / 没有评论 / 805浏览

前言

随着公司近期使用springboot用户越来越多,遇到的问题也越来越多了。 经常有同事问为什么按照网上的配置不生效了,甚至一度怀疑application.yml配置的作用。本文解释一下可能出现这问题的原因。

日期格式引起的问题

某天开发一个后台类的项目,发现一个date类型的字段返回是long类型的时间戳。于是按照文档的配置,在yml文件添加了如下配置,将jackson序列化日期改成yyyy-MM-dd HH🇲🇲ss模式。

spring:
 jackson:
  date-format: yyyy-MM-dd HH🇲🇲ss
  serialization:
   write-dates-as-timestamps: false

重启后发现不生效。

EnableWebMvc引起的血案

通过查看源码,发现这个配置是定义在JacksonProperties类中, 通过断点JacksonAutoConfiguration类:


@Override
public void customize(Jackson2ObjectMapperBuilder builder) {
  if(this.jacksonProperties.getDefaultPropertyInclusion() != null) {
       builder.serializationInclusion(this.jacksonProperties.getDefaultPropertyInclusion();
  }
  if (this.jacksonProperties.getTimeZone() != null) {
     builder.timeZone(this.jacksonProperties.getTimeZone());
  }
  configureFeatures(builder, FEATURE_DEFAULTS);				
  configureVisibility(builder,this.jacksonProperties.getVisibility());
  configureFeatures(builder, this.jacksonProperties.getDeserialization());
  configureFeatures(builder, this.jacksonProperties.getSerialization());
  configureFeatures(builder, this.jacksonProperties.getMapper());
  configureFeatures(builder, this.jacksonProperties.getParser());
  configureFeatures(builder, this.jacksonProperties.getGenerator());
  configureDateFormat(builder);
  configurePropertyNamingStrategy(builder);
  configureModules(builder);
  configureLocale(builder);
}

发现配置的format信息已经生效并且设置到builder里了。

而springboot的MessageConverter是通过 HttpMessageConvertersAutoConfiguration的如下方法生成Bean:

  @Bean
  @ConditionalOnMissingBean
  public HttpMessageConverters messageConverters() {
    return new HttpMessageConverters(this.converters);
  }

我们通过断点发现这个方法并没有进来,由此推断@ConditionalOnMissingBean注解生效,也就是beanFactory中存在HttpMessageConverters这个类型的Bean了。

我们都知道,WebMvcConfigurer接口的configureMessageConverters方法是配置MessageConverter地方

default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}

通常情况下SpringBoot是通过WebMvcAutoConfiguration类中的内部类WebMvcAutoConfigurationAdapter来进行这个操作,此时初步推断WebMvcAutoConfiguration没有装配成功。 从如下的代码,我们可以看到@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
		TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

此时基本找到问题的所在,也就是上下文中存在WebMvcConfigurationSupport类型的bean。 经过排查,发现项目中仅仅存在如下的配置类。

@Configuration
@EnableWebMvc
public class AutoConfiguration implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
    }
}

虽然并没有继承WebMvcConfigurationSupport,但是存在一个注解@EnableWebMvc。我们查看这个注解的源码:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

import了一个DelegatingWebMvcConfiguration。而此类恰好继承了上面所提到的类。

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

继承WebMvcConfigurationSupport类后如果没自定义MessageConvert,则通过addDefaultHttpMessageConverters方法添加默认的转换器。

类似以上的情况还有 自动配置的静态资源路径(classpath:/META/resources/,classpath:/resources/,classpath:/static/,classpath:/public/)资源全部失效。

对此springboot官网也有说明:

Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration

结论

1、使用@EnableWebMvc 注解会屏蔽springboot的@EnableAutoConfiguration中的设置。

2、继承WebMvcConfigurationSupport的Bean会屏蔽@EnableAutoConfiguration中的设置。

3、当我们需要添加自定义拦截器可以implements WebMvcConfigurer在扩展的类中重写父类的方法即可。