问题现象
/**
* fastjson中$ref对象重复引用问题
*/
public class TestFastJson {
List<Person> list = new ArrayList<Person>();
Person p = new Person("FLY", 25);
/**
* 测试main
*/
public static void main(String[] args) {
new TestFastJson().test1();
}
/**
* 未关闭引用检测,转化字符串时会出现$ref
*/
public void test1() {
list.add(p);
list.add(p);
String jsonStr = JSON.toJSONString(list);
System.out.println(jsonStr);
// [{"age":25,"name":"FLY"},{"$ref":"$[0]"}]
}
}
class Person {
private String name;
private int age;
// 构造函数
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
问题分析
- 实体转化为json字符串后出现了$ref字样的东西,这是因为在传输的数据中出现相同的对象时,fastjson默认开启引用检测将相同的对象写成引用的形式
- 引用是通过"$ref"来表示的
引用 | 描述 |
---|---|
“$ref”:“…” | 上一级 |
“$ref”:“@” | 当前对象,也就是自引用 |
“ r e f " : " ref":" ref":"” | 根对象 |
“ r e f " : " ref":" ref":".children.0” | 基于路径的引用,相当于 root.getChildren().get(0) |
- 说到引用分为两种,重复引用和循环引用
循环引用:即A对象引用B对象,B对象又引用A对象,这种情况是要极力避免的,因为会导致堆栈溢出(StackOverflowError);
重复引用:上面的例子就是因为相同的订单对象出现在两个集合中,所以第二个orderList集合中直接返回的是$ref
。一般大家在写代码的过程中,如果出现$ref
,通常应该是重复引用问题。
问题解决
1.局部关闭
将该对象在后端转换为json字符串返回给前端,使用SerializerFeature.DisableCircularReferenceDetect关闭循环引用。
String jsonString=JSON.toJSONString(object, SerializerFeature.DisableCircularReferenceDetect);
2.全局配置关闭
可以在SpringBoot项目的json配置中将循环引用关闭。FastJson的.java配置增加以下项:
fastConverter.setFeatures(SerializerFeature.DisableCircularReferenceDetect);
or
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
但是因为全局配置是在我们项目的基础jar包中配置的,改动基础jar包会有风险,会对前面所有的依赖项目产生影响。所以也不采用这种方式。
大家想想为什么fastjson默认是开启这个功能的就知道原因了。如果全局关闭,性能也会有极大影响。
3.new新的对象来避免
我们可以将List中的对象使用BeanUtil这样的工具,拷贝为新的对象。(BeanUtil创建出来的对象跟原来的对象不是同一个对象)
4.禁止序列化
如果循环引用的数据,前端用不到,那可以在实体类对应的字段加注解禁止序列化,这样前端就不会接收到这个字段的引用数据了。
@JSONField(serialize = false)
private List<Order> orderList;
5.在该字段的注解上指定序列化时关闭循环引用
但是很多时候,前端又需要这个数据,所以禁止序列化也是不行滴,接下来看另外一种注解解决方式。
@JSONField(serialzeFeatures = {SerializerFeature.DisableCircularReferenceDetect})
private List<Object> objectList;