多个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);
        }
    }
}

  这样我们就会把引入的其他的给覆盖掉了。

posted @ 2025-12-18 14:13  Doyourself!  阅读(1)  评论(0)    收藏  举报