Java对象内部循环引用,导致Fastjson2序列化对象时StackOverflowError
Java项目中有用Fastjson2序列化一个map的代码,当map中有一个自定义的InvalidParameterException对象时会StackOverflowError。
简化一下如下:
import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.x.x.x.x.InvalidParameterException;
import org.junit.jupiter.api.Test;
public class JsonErrorTest {
/**
* 测试Fastjson2
*/
@Test
public void fastjson2Test() {
InvalidParameterException exception = new InvalidParameterException("test");
JSON.toJSONString(exception);
}
/**
* 测试Jackson
*/
@Test
public void jacksonTest() throws JsonProcessingException {
InvalidParameterException exception = new InvalidParameterException("test");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValueAsString(exception);
}
}
测试Fastjson2结果如下:
java.lang.StackOverflowError
at com.alibaba.fastjson2.writer.FieldWriterObject.write(FieldWriterObject.java:205)
at com.alibaba.fastjson2.writer.ObjectWriterException.write(ObjectWriterException.java:45)
at com.alibaba.fastjson2.writer.FieldWriterObject.write(FieldWriterObject.java:327)
at com.alibaba.fastjson2.writer.ObjectWriterException.write(ObjectWriterException.java:45)
at com.alibaba.fastjson2.writer.FieldWriterObject.write(FieldWriterObject.java:327)
at com.alibaba.fastjson2.writer.ObjectWriterException.write(ObjectWriterException.java:45)
at com.alibaba.fastjson2.writer.FieldWriterObject.write(FieldWriterObject.java:327)
at com.alibaba.fastjson2.writer.ObjectWriterException.write(ObjectWriterException.java:45)
at com.alibaba.fastjson2.writer.FieldWriterObject.write(FieldWriterObject.java:327)
at com.alibaba.fastjson2.writer.ObjectWriterException.write(ObjectWriterException.java:45)
at com.alibaba.fastjson2.writer.FieldWriterObject.write(FieldWriterObject.java:327)
看到这个报错一开始不知道是什么原因。
测试Jackson结果如下:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Direct self-reference leading to cycle (through reference chain: com.x.x.x.x.InvalidParameterException["rootCause"])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)
at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1277)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter._handleSelfReference(BeanPropertyWriter.java:945)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:722)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:755)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
at com.fasterxml.jackson.databind.ObjectMapper._writeValueAndClose(ObjectMapper.java:4407)
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3661)
可以看到Jackson的报错提示很清晰:com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Direct self-reference leading to cycle (through reference chain: com.x.x.x.x.InvalidParameterException["rootCause"])
是“rootCause”字段引用自己(this)导致的循环引用。通过查看代码,异常类中的确有这样一段代码
public Throwable getRootCause() {
Throwable rootCause = this;
Throwable cause = getCause();
while (cause != null && cause != rootCause) {
rootCause = cause;
cause = cause.getCause();
}
return rootCause;
}
这是一个get方法,json框架会序列化为rootCause字段,这个方法有可能返回自身,所以造成了循环引用。
解决方法:
1.(推荐)修改类代码,不要循环引用。
2.(不推荐)对于Fastjson2使用框架的检测循环引用的参数来避免报错,代码如下:
/**
* 测试Fastjson2
*/
@Test
public void fastjson2Test() {
InvalidParameterException exception = new InvalidParameterException("test");
JSON.toJSONString(exception, JSONWriter.Feature.ReferenceDetection);
}
对于Jackson,可以在循环引用字段上加上这些注解@JsonIgnore, @JsonBackReference