如何正确使用@JsonIdentityInfo解决Jackson序列化循环依赖问题
解决Java类循环依赖的Jackson序列化问题
你的三个类存在双向关联:Process ↔ User、Process ↔ AnsweredQuestionnaire,这会导致Jackson序列化时无限递归,最终抛出栈溢出异常。@JsonIdentityInfo是解决这类问题的核心方案,下面是正确的用法和修改后的代码:
核心原理
@JsonIdentityInfo的作用是给每个对象分配唯一标识:第一次序列化对象时输出完整结构,后续再遇到同一个对象时,只输出它的标识ID,避免重复遍历循环关联的对象。
因为你的类已经有自己的UUID主键(比如processId、userId),优先使用对象自身的主键作为标识,比Jackson自动生成的UUID更直观,也更符合业务逻辑。
修改后的代码实现
Process.java
@Getter @Setter @NoArgsConstructor @AllArgsConstructor // 使用自身的processId作为唯一标识 @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "processId") public class Process { private UUID processId; private List<User> users = new ArrayList<>(); private List<UnitType> units = new ArrayList<>(); private String furtherComment; private List<AnsweredQuestionnaire> answeredQuestionnaires = new ArrayList<>(); @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Process)) return false; Process process = (Process) o; return getProcessId().equals(process.getProcessId()); } @Override public int hashCode() { return Objects.hash(getProcessId()); } }
AnsweredQuestionnaire.java
@Setter @Getter @NoArgsConstructor @AllArgsConstructor // 使用自身的answeredQuestionnaireId作为唯一标识 @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "answeredQuestionnaireId") public class AnsweredQuestionnaire { private UUID answeredQuestionnaireId; private Questionnaire questionnaire; private Process process; public void addProcessToAnsweredQuestionnaire(Process process){ //remove old association if(this.process != null){ this.process.getAnsweredQuestionnaires().remove(this); } this.process = process; //add new association if(process != null){ this.process.getAnsweredQuestionnaires().add(this); } } }
User.java
@Setter @Getter @NoArgsConstructor @AllArgsConstructor // 使用自身的userId作为唯一标识 @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "userId") public class User { private UUID userId; private String firstName; private String lastName; private String phoneNumber; private String email; private List<Role> roles = new ArrayList<>(); private List<Process> processes = new ArrayList<>(); public void addProcessToUser(Process process){ this.processes.add(process); process.getUsers().add(this); } public void removeProcessFromUser(Process process){ this.processes.remove(process); process.getUsers().remove(this); } }
效果说明
当你序列化一个包含关联关系的对象时,比如:
// 创建测试对象 User user = new User(UUID.randomUUID(), "John", "Doe", "123456", "john@example.com", new ArrayList<>(), new ArrayList<>()); Process process = new Process(UUID.randomUUID(), new ArrayList<>(), new ArrayList<>(), "Test comment", new ArrayList<>()); AnsweredQuestionnaire aq = new AnsweredQuestionnaire(UUID.randomUUID(), null, null); // 建立关联 user.addProcessToUser(process); aq.addProcessToAnsweredQuestionnaire(process); // 序列化 ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(process);
输出的JSON会是这样的结构:
- 第一次出现
Process时,输出完整的字段,包括它的users和answeredQuestionnaires列表 - 列表中的
User和AnsweredQuestionnaire会输出完整结构,而它们内部引用的Process只会输出processId的值,不会重复序列化整个Process对象
这样就彻底解决了循环依赖导致的序列化栈溢出问题,同时反序列化时Jackson也能根据ID正确重建对象间的关联关系。
内容的提问来源于stack exchange,提问作者Syed Iftekharuddin
相关产品推荐
相关产品推荐

