https://segmentfault.com/a/1190000015975405
記一次踩坑:springboot2.0.2配置fastjson不生效
最近在嘗試搭建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
中的write
和writeInternal
打了斷點,再次執行,竟然什么都沒有發生,根本沒有走這兩個方法,於是在自定義的SpringMvcConfigure
中configureMessageConverters
方法內打了斷點,想看看這個方法參數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
還是添加進去了,但是由於FastJsonHttpMessageConverter
在MappingJackson2HttpMessageConverter
之前添加,所以對結果不影響.至此,解決了這個問題.
總結
- 最重要的還是解決了springboot2.0.2配置fastjson不生效的問題
- 更加明白stream api返回的都是全新的對象
- 更理解java是值傳遞而不是引用傳遞
- 了解到想要在迭代過程中對集合進行操作要用Iterator,而不是直接簡單的for循環或者增強for循環
如有不正確的地方還請指出,謝謝.