zoukankan      html  css  js  c++  java
  • springboot自定义消息转换器HttpMessageConverter

    https://segmentfault.com/a/1190000015975405

    记一次踩坑:springboot2.0.2配置fastjson不生效

    更新于 2018-08-13  约 18 分钟

    最近在尝试搭建springboot+dubbo+shiro基于注解的一个项目,突发奇想想把消息转换器从jackson换成fastjson,于是就开始了折腾之路.

    轻车熟路的去自定了一个SpringMvcConfigure去继承WebMvcConfigurerAdapter,然后就发现这个WebMvcConfigurerAdapter竟然过时了?what?点进去看源码:

    /**
     * An implementation of {@link WebMvcConfigurer} with empty methods allowing
     * subclasses to override only the methods they're interested in.
     *
     * @author Rossen Stoyanchev
     * @since 3.1
     * @deprecated as of 5.0 {@link WebMvcConfigurer} has default methods (made
     * possible by a Java 8 baseline) and can be implemented directly without the
     * need for this adapter
     */
    @Deprecated
    public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {}

    可以看到从spring5.0开始就被@Deprecated,原来是java8中支持接口中有默认方法,所以我们现在可以直接实现WebMvcConfigurer,然后选择性的去重写某个方法,而不用实现它的所有方法.

    于是就实现了WebMvcConfigurer:

    @Configuration
    public class SpringMvcConfigure implements WebMvcConfigurer {
    
        /**
         * 配置消息转换器
         * @param converters
         */
        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
            //自定义配置...
            FastJsonConfig config = new FastJsonConfig();
            config.setSerializerFeatures(SerializerFeature.QuoteFieldNames,
                    SerializerFeature.WriteEnumUsingToString,
                    /*SerializerFeature.WriteMapNullValue,*/
                    SerializerFeature.WriteDateUseDateFormat,
                    SerializerFeature.DisableCircularReferenceDetect);
            fastJsonHttpMessageConverter.setFastJsonConfig(config);
            converters.add(fastJsonHttpMessageConverter);
        }
    
    }

    本以为这样子配置就可以完事儿,但是诡异的事情发生了,我明明注释了SerializerFeature.WriteMapNullValue,可是返回的json中仍然有为null的字段,然后我就去com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter中的writewriteInternal打了断点,再次执行,竟然什么都没有发生,根本没有走这两个方法,于是在自定义的SpringMvcConfigureconfigureMessageConverters方法内打了断点,想看看这个方法参数converters里边到底有什么:
    converters

    看到这里就想到,肯定是我自己添加的fastjson在后边,所以没有生效,所以就加了以下代码:

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters = converters.stream()
                    .filter((converter)-> !(converter instanceof MappingJackson2HttpMessageConverter))
                    .collect(Collectors.toList());
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
        //自定义配置...
        FastJsonConfig config = new FastJsonConfig();
        config.setSerializerFeatures(SerializerFeature.QuoteFieldNames,
                SerializerFeature.WriteEnumUsingToString,
                /*SerializerFeature.WriteMapNullValue,*/
                SerializerFeature.WriteDateUseDateFormat,
                SerializerFeature.DisableCircularReferenceDetect);
        fastJsonHttpMessageConverter.setFastJsonConfig(config);
        converters.add(fastJsonHttpMessageConverter);
    }

    竟然还没有生效,后来开始追踪,开始方法是从org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport类中的一个bean配置:

    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
        RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
        adapter.setContentNegotiationManager(mvcContentNegotiationManager());
        //就是从这里开始设置converters的,然后从这里一路追踪下去.
        adapter.setMessageConverters(getMessageConverters());
        adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
        adapter.setCustomArgumentResolvers(getArgumentResolvers());
        adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
    
        if (jackson2Present) {
            adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
            adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
        }
    
        AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
        configureAsyncSupport(configurer);
        if (configurer.getTaskExecutor() != null) {
            adapter.setTaskExecutor(configurer.getTaskExecutor());
        }
        if (configurer.getTimeout() != null) {
            adapter.setAsyncRequestTimeout(configurer.getTimeout());
        }
        adapter.setCallableInterceptors(configurer.getCallableInterceptors());
        adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());
    
        return adapter;
    }

    getMessageConverters()方法:

    protected final List<HttpMessageConverter<?>> getMessageConverters() {
        if (this.messageConverters == null) {
            this.messageConverters = new ArrayList<>();
            configureMessageConverters(this.messageConverters);//进入这一步
            if (this.messageConverters.isEmpty()) {
                addDefaultHttpMessageConverters(this.messageConverters);
            }
            extendMessageConverters(this.messageConverters);
        }
        return this.messageConverters;
    }

    org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration:

    @Override
    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        this.configurers.configureMessageConverters(converters);
    }
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.configureMessageConverters(converters);
        }
    }

    this.delegates包含了springboot的一个默认配置类类:org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration里边有一个参数

    private final HttpMessageConverters messageConverters;

    for循环里的delegate.configureMessageConverters(converters)调用了WebMvcAutoConfiguration中的configureMessageConverters方法:

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.addAll(this.messageConverters.getConverters());
    }

    执行完这个后,就给converters中添加了10个转换器了,就是上图中的10个.
    this.delegates中还有一个就是我们自定义的那个,执行完后,在我们自定义的那个SpringMvcConfigure发现我添加的fastjson添加进去了,但是org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.getMessageConverters(),发现converters并没有发现我们添加进去的FastJsonHttpMessageConverter,这时突然又想起来:java8的stream api每次都是生成一个新的对象,所以导致converters已经不是传递过来的那个converters的引用了(这里也证明了java是值传递,不是引用传递).

    于是再次改变那个lambda表达式为普通的增强for循环:

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        /*converters = converters.stream().
                filter((converter)-> !(converter instanceof MappingJackson2HttpMessageConverter))
                .collect(Collectors.toList());*/
        for (HttpMessageConverter<?> converter : converters) {
            if (converter instanceof MappingJackson2HttpMessageConverter){
                converters.remove(converter);
            }
        }
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
        //自定义配置...
        FastJsonConfig config = new FastJsonConfig();
        config.setSerializerFeatures(SerializerFeature.QuoteFieldNames,
                SerializerFeature.WriteEnumUsingToString,
                /*SerializerFeature.WriteMapNullValue,*/
                SerializerFeature.WriteDateUseDateFormat,
                SerializerFeature.DisableCircularReferenceDetect);
        fastJsonHttpMessageConverter.setFastJsonConfig(config);
        converters.add(fastJsonHttpMessageConverter);
    }

    再次运行,wtf?报错了:ConcurrentModificationException,原来使用for循环遍历过程中不能进行remove操作,于是换成Iterator:

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        /*converters = converters.stream()
                .filter((converter)-> !(converter instanceof MappingJackson2HttpMessageConverter))
                .collect(Collectors.toList());
        for (HttpMessageConverter<?> converter : converters) {
            if (converter instanceof MappingJackson2HttpMessageConverter){
                converters.remove(converter);
            }
        }*/
        Iterator<HttpMessageConverter<?>> iterator = converters.iterator();
        while(iterator.hasNext()){
            HttpMessageConverter<?> converter = iterator.next();
            if(converter instanceof MappingJackson2HttpMessageConverter){
                iterator.remove();
            }
        }
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
        //自定义配置...
        FastJsonConfig config = new FastJsonConfig();
        config.setSerializerFeatures(SerializerFeature.QuoteFieldNames,
                SerializerFeature.WriteEnumUsingToString,
                /*SerializerFeature.WriteMapNullValue,*/
                SerializerFeature.WriteDateUseDateFormat,
                SerializerFeature.DisableCircularReferenceDetect);
        fastJsonHttpMessageConverter.setFastJsonConfig(config);
        converters.add(fastJsonHttpMessageConverter);
    }

    再次运行,我去,终于解决了,先是删除MappingJackson2HttpMessageConverter,然后添加FastJsonHttpMessageConverter,但是不是到为什么进过一系列操作后,MappingJackson2HttpMessageConverter还是添加进去了,但是由于FastJsonHttpMessageConverterMappingJackson2HttpMessageConverter之前添加,所以对结果不影响.至此,解决了这个问题.

    总结

    1. 最重要的还是解决了springboot2.0.2配置fastjson不生效的问题
    2. 更加明白stream api返回的都是全新的对象
    3. 更理解java是值传递而不是引用传递
    4. 了解到想要在迭代过程中对集合进行操作要用Iterator,而不是直接简单的for循环或者增强for循环

    如有不正确的地方还请指出,谢谢.

  • 相关阅读:
    Axel linux下多线程下载工具
    使用Scala编写Spark程序求基站下移动用户停留时长TopN
    编写Spark的WordCount程序并提交到集群运行[含scala和java两个版本]
    使用Eclipse编译运行MapReduce程序 Hadoop2.6.0_Ubuntu/CentOS
    Eclipse上Hadoop插件中Run On Hadoop原理[转]
    apache官方中文hadoop说明文档地址
    如何在Windows下面运行hadoop的MapReduce程序
    通过web界面查看hadoop集群运行日志的地址
    linux命令-查看当前目录下所有文件的大小:“ll -h”
    BZOJ3979 : [WF2012]infiltration
  • 原文地址:https://www.cnblogs.com/xiang--liu/p/12606232.html
Copyright © 2011-2022 走看看