0%

spring-bug-list

1. 重定义 WebMvcConfigurationSupport 导致格式转换失效

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration(proxyBeanMethods = false)
public class WebMvcConfig extends WebMvcConfigurationSupport {

@Override
public void addInterceptors(InterceptorRegistry registry) {
// ...添加自定义拦截器
}

@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
// ...添加静态资源处理器
}
}

如上代码, 由于继承了 WebMvcConfigurationSupport, 导致 WebMvcAutoConfiguration 不加载, 从而导致一些自定义的格式化等配置失效.

1.1. 原因

1
2
3
4
5
6
7
8
9
10
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) // 存在对应类型的 Bean 则不加载
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
// ...
}

1.2. 解决方案

1.2.1. 改用实现 WebMvcConfigurer

通过 WebMvcConfigurer 替代 WebMvcConfigurationSupport
建议使用次方案.

1
2
3
4
5
6
7
8
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
// ...添加自定义拦截器
}
}

添加处理器则可以参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
@Import({ SpringfoxWebMvcConfiguration.class, SwaggerCommonConfiguration.class })
@ComponentScan(basePackages = {
"springfox.documentation.swagger2.mappers"
})
@ConditionalOnWebApplication
public class Swagger2DocumentationConfiguration {
@Bean
public JacksonModuleRegistrar swagger2Module() {
return new Swagger2JacksonModule();
}

@Bean
public HandlerMapping swagger2ControllerMapping(
Environment environment,
DocumentationCache documentationCache,
ServiceModelToSwagger2Mapper mapper,
JsonSerializer jsonSerializer) {
return new PropertySourcedRequestMappingHandlerMapping(
environment,
new Swagger2Controller(environment, documentationCache, mapper, jsonSerializer));
}
}

1.2.2. 继续添加定义格式化等配置

该方案比较繁琐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
@Configuration
public class DateConfig {

@Bean
public Converter<String, LocalDate> localDateConverter() {
return new Converter<String, LocalDate>() {
@Override
public LocalDate convert(String source) {
return LocalDate.parse(source, DateTimeFormatter.ofPattern(Constants.DATE_PATTERN));
}
};
}

@Bean
public Converter<String, LocalDateTime> localDateTimeConverter() {
return new Converter<String, LocalDateTime>() {
@Override
public LocalDateTime convert(String source) {
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(Constants.DATETIME_PATTERN));
}
};
}

}

@Configuration
public class JacksonConfig {

@Bean
@Primary
public ObjectMapper jacksonObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(Constants.DATETIME_PATTERN)));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(Constants.DATETIME_PATTERN)));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(Constants.DATE_PATTERN)));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(Constants.DATE_PATTERN)));
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}

// @Bean
// public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
// return builder -> {
// builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(Constants.DATETIME_PATTERN)));
// builder.serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(Constants.DATE_PATTERN)));
// builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(Constants.DATETIME_PATTERN)));
// builder.deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(Constants.DATE_PATTERN)));
// };
// }

}

@Configuration(proxyBeanMethods = false)
public class WebMvcConfig extends WebMvcConfigurationSupport {

@Autowired
private MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter;

@Autowired
private Converter<String, LocalDate> localDateConverter;

@Autowired
private Converter<String, LocalDateTime> localDateTimeConverter;


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

registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}

@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
super.extendMessageConverters(converters);
for (int i = 0; i < converters.size(); i++) {
if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) {
converters.set(i, mappingJackson2HttpMessageConverter);
return;
}
}
converters.add(mappingJackson2HttpMessageConverter);
}

@Override
protected void addFormatters(FormatterRegistry registry) {
super.addFormatters(registry);
registry.addConverter(localDateConverter);
registry.addConverter(localDateTimeConverter);
}
}