增加配置:
package com.wm.auth.config;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.List;
/***
* @ClassName: WebAppConfigurer
* @Description: 跨域本地測試, 線上使用nginx處理
* @Author: wm_yu
* @Create_time: 15:01 2020-3-13
*/
@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
/**
* 跨域處理
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
//.allowedOrigins("http://192.168.89.89")
//rest集中請求方式
.allowedMethods("GET", "POST","DELETE")
.allowCredentials(false).maxAge(36000);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Iterator<HttpMessageConverter<?>> iterator = converters.iterator();
while(iterator.hasNext()){
HttpMessageConverter<?> converter = iterator.next();
if(converter instanceof MappingJackson2HttpMessageConverter){
iterator.remove();
}
}
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// 將long類型的數據轉爲String類型
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
simpleModule.addSerializer(long.class, ToStringSerializer.instance);
mapper.registerModule(simpleModule);
converter.setObjectMapper(mapper);
converters.add(converter);
}
}
效果:
{
"code": 200,
"msg": "操作成功",
"data": [
{
"id": "4",
"parentId": "-1",
"sort": 0,
"type": "md-home",
"createDate": "2020-03-17 11:49:28",
"modifyDate": "2020-03-17 11:49:28",
"delFlag": 0,
"text": "系統管理",
"children": [
{
"id": "5",
"name": "menu",
"parentId": "4",
"sort": 1,
"type": "md-home",
"createDate": "2020-03-17 14:02:20",
"modifyDate": "2020-03-17 14:02:20",
"delFlag": 0,
"text": "菜單管理"
},
{
"id": "1240211721989435393",
"name": "roleAdmin",
"parentId": "4",
"sort": 2,
"type": "md-home",
"createDate": "2020-03-18 17:41:31",
"modifyDate": "2020-03-18 17:41:31",
"delFlag": 0,
"text": "角色管理"
}
]
},
{
"id": "3",
"name": "home",
"parentId": "-1",
"sort": 1,
"type": "md-home",
"createDate": "2020-03-17 11:34:07",
"modifyDate": "2020-03-17 11:34:07",
"delFlag": 0,
"text": "主頁q"
},
{
"id": "1",
"parentId": "-1",
"sort": 2,
"type": "md-home",
"createDate": "2020-03-17 11:19:35",
"modifyDate": "2020-03-17 11:19:35",
"delFlag": 0,
"text": "二級菜單啊",
"children": [
{
"id": "2",
"name": "t1",
"parentId": "1",
"sort": 1,
"type": "md-home",
"createDate": "2020-03-17 11:20:06",
"modifyDate": "2020-03-17 11:20:06",
"delFlag": 0,
"text": "表格"
}
]
}
]
}
可以看到完全去除了,null值/空值的屬性
下面是轉載的部分
首先在網絡上查看資料,開始配置時,老是不生效,最後看到這位大佬的文章:
記一次踩坑: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循環
如有不正確的地方還請指出,謝謝.