问题现象:
在调用mq去发送短信时,使用了一个jsonString对象去传递参数。发现消费方接收到的jsonString解析出来之后,只有第一个对象的参数有值,后序的参数中出现了$ref。模拟入参场景类似如下
HashMap<String, String> map = Maps.newHashMap();
map.put("mobile", "111111");
List<Object> list = Lists.newArrayList();
list.add(new SmsDTO("内容1", map));
list.add(new SmsDTO("内容2", map));
list.add(new SmsDTO("内容3", map));
list.add(new SmsDTO("内容4", map));
String jsonString = JSON.toJSONString(list);
//调用消息发送方
msg.send(jsonString);
在该场景下,list序列化之后的字符串出现了$ref,具体如下
从上图可以看出,除了第一条数据的参数是正常的,后续的params都出现了$ref。
原因:
其实这个是fastjson的一个机制,当发现需要序列化的对象中有重复的对象,会使用对象的引用来表示。这个是fastjson为了避免出现json序列化的循环引用的一个特性。
// 循环引用的特殊情况,自引用
Map<String,Object> map = new HashMap<>();
map.put("map",map);
//
// map1引用了map2,而map2又引用map1,导致循环引用
Map<String,Object> map1 = new HashMap<>();
Map<String,Object> map2 = new HashMap<>();
map1.put("map",map2);
map2.put("map",map1);
如果碰到以上情况,就会出现内循环,导致出现StackOverFlow异常。作为一个公共组件,fastjson必须保障自身的安全性,所以这些就需要我们从自身代码进行出发改造。
解决办法:
由于循环引用是默认开启的,所以最简单的就是关闭这个特性
JSON.toJSONString(list, SerializerFeature.DisableCircularReferenceDetect);
或者是全局关闭,以springboot为例,通过自定义converter来解决
public class FastJsonHttpMessageConverterEx extends FastJsonHttpMessageConverter{
public FastJsonHttpMessageConverterEx(){
//在这里配置fastjson特性(全局设置的)
FastJsonConfig fastJsonConfig = new FastJsonConfig();
//关闭循环引用
fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
this.setFastJsonConfig(fastJsonConfig);
}
@Override
protected boolean supports(Class<?> clazz) {
return super.supports(clazz);
}
}
@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
.....
@Bean
public FastJsonHttpMessageConverterEx fastJsonHttpMessageConverterEx(){
return new FastJsonHttpMessageConverterEx();
}
}
以上的方法处理后,都无法避免循环引用的问题。回到原因上,fastjson的这个特性的目的其实就是对于相同对象的一个特殊处理。所以最好的解决办法就是传入不同的对象,彻底避免这个问题。
List<Object> list = new ArrayList<>();
Object obj = new Object();
list.add(obj);
// 创建新的对象
Object newObj = new Object();
// 使用org.springframework.beans.BeansUtils复制属性值
BeansUtils.copyProperties(obj, newObj);
list.add(newObj);
使用对象拷贝的方式避免,是遵守fastjson安全机制的最佳处理方法