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

posted @ 2025-03-20 16:27  年迈的魔法师  阅读(230)  评论(0)    收藏  举报