多个WebMvcConfigurer配置Jackson2ObjectMapperBuilder不生效问题记录
项目里面有主键采用雪花算法生成Long类型的主键id,但是返回给端上时候如果超过16位,可能会导致Long精度丢失的问题,一般的处理思路是把Long型转化为String类型来处理。如果Dto比较少的话,可以直接在对应的属性上面添加如下注解:
@JsonSerialize(using = ToStringSerializer.class) private Long directoryId;
这样就可以解决,但是如果项目里面有大量的dto,一个个添加起来不仅费事,而且还可能添加不全。这个时候就可以通过 implement WebMvcConfigurer来全局自定义。一般的配置如下即可:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.clear();
//设置转换器格式,和时区。
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
.indentOutput(true)
.dateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
.timeZone(TimeZone.getTimeZone("Asia/Shanghai"))
.modulesToInstall(new ParameterNamesModule())
.serializerByType(Long.TYPE, ToStringSerializer.instance)
.serializerByType(Long.class, ToStringSerializer.instance);
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
这样的话,就可以达到全局配置的效果。但是有时候项目里面会有比较奇怪的事情出现,比如其他模块里面也是用了 implements WebMvcConfigurer 而且还是通过pom依赖的方式引入的,比如当前项目pom依赖一个cusc-web的模块,刚好这个模块里面也有类似的使用:
package com.cusc.nirvana.web.converter; import com.cusc.nirvana.web.interceptor.AppGlobalHandlerInterceptor; import java.util.List; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.http.CacheControl; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.mvc.WebContentInterceptor; @AutoConfiguration public class AppGlobalWebMvcConfigurerAdapter implements WebMvcConfigurer, AppHttpMessageConverter { public void addInterceptors(InterceptorRegistry registry) { WebContentInterceptor webContentInterceptor = new WebContentInterceptor(); CacheControl nocache = CacheControl.noCache(); webContentInterceptor.addCacheMapping(nocache, new String[]{"/**"}); registry.addInterceptor(webContentInterceptor); registry.addInterceptor(new AppGlobalHandlerInterceptor()).excludePathPatterns(new String[]{"/swagger*", "/webjars/**", "/v3/api-docs"}); super.addInterceptors(registry); } public void configurePathMatch(PathMatchConfigurer configurer) { configurer.setUseSuffixPatternMatch(false); } public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { this.setAppHttpMessageConverter(converters); } }
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package com.cusc.nirvana.web.converter; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import java.util.List; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageConverter; public interface AppHttpMessageConverter { default List<HttpMessageConverter<?>> setAppHttpMessageConverter(List<HttpMessageConverter<?>> converters) { converters.removeIf((converter) -> converter instanceof MappingJackson2HttpMessageConverter || converter instanceof MappingJackson2SmileHttpMessageConverter || converter instanceof MappingJackson2CborHttpMessageConverter || converter instanceof FastJsonHttpMessageConverter); if (this.fastJsonSerializationByTimestamp()) { converters.add(fastJsonHttpMessageConverterTimestamp()); } else { converters.add(fastJsonHttpMessageConverterUTC()); } return converters; } default boolean fastJsonSerializationByTimestamp() { return false; } static HttpMessageConverter fastJsonHttpMessageConverterUTC() { return AppHttpMessageConverterProvider.fastJsonHttpMessageConverterUTC(); } static HttpMessageConverter fastJsonHttpMessageConverterTimestamp() { return AppHttpMessageConverterProvider.fastJsonHttpMessageConverterTimestamp(); } }
这个时候就发现,明明我们写的WebConfig 已经加载到spring bean里面了,但是为啥接口测试时候的数据仍然没有转换呢?
通过断点调试发现,spring在加载时候每次都是先加载我们自己的public class WebConfig implements WebMvcConfigurer ,然后才去加载
AppGlobalWebMvcConfigurerAdapter ,里面AppHttpMessageConverter 就会覆盖掉我们之前的设置。导致我们的设置不生效,即使我在我们自己的配置类上面添加了@Order(Ordered.LOWEST_PRECEDENCE),但是仍然会覆盖掉我们的配置。
这个时候的一个方案就是:在spring boot启动成功前再去加载我们的消息转换器:
@Component
@Slf4j
public class StartInitListener implements CommandLineRunner {
@Autowired
private RequestMappingHandlerAdapter adapter;
@Override
public void run(String... args) throws Exception {
try {
//由于cusc-web的依赖包里面把MappingJackson2HttpMessageConverter去掉了(详见:cusc-web依赖的 AppGlobalWebMvcConfigurerAdapter以及AppHttpMessageConverter
// 且其是后加载的,导致WebConfig里面的设置被覆盖且过滤掉了MappingJackson2HttpMessageConverter,解决方案是最后重置jackson并且添加)
adapter.getMessageConverters().clear();
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
.indentOutput(true)
.dateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
.timeZone(TimeZone.getTimeZone("Asia/Shanghai"))
.modulesToInstall(new ParameterNamesModule())
.serializerByType(Long.TYPE, ToStringSerializer.instance)
.serializerByType(Long.class, ToStringSerializer.instance);
adapter.getMessageConverters().add(new MappingJackson2HttpMessageConverter(builder.build()));
} catch (Exception e) {
log.error("启动异常",e);
}
}
}
这样我们就会把引入的其他的给覆盖掉了。

浙公网安备 33010602011771号