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循環

如有不正確的地方還請指出,謝謝.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM