CXF JAX-RS数据绑定时自引用POJO转JSON出现StackOverflowError如何解决
问题根因
报错是Domain类中parent和subdomains构成双向自引用,默认的JAXB JSON序列化器在处理时会无限递归展开关联对象,最终触发栈溢出。另外接口代码存在两处明显笔误:@Path定义的路径变量是{id},但@PathParam绑定的是uuid,运行时无法正确获取传入参数;方法返回值声明为Bus,和实际返回的Domain类型不匹配,也会引发额外的类型转换错误。
解决方案
可以根据业务需求选择以下任意一种方案处理循环引用问题:
- 方案1:用
@XmlTransient注解忽略单侧关联字段
如果业务不需要序列化其中一侧的关联关系,直接在对应字段上加@XmlTransient即可打断循环,示例如下:
@XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "") @XmlRootElement(name = "domain") @Entity class Domain { @XmlTransient // 序列化时忽略parent字段,打断循环 @ManyToOne private Domain parent; @OneToMany private List<Domain> subdomains; // 其他字段、getter/setter省略 }
如果需要保留parent、不需要subdomains,也可以把@XmlTransient加在subdomains字段上。
- 方案2:用
@XmlID+@XmlIDREF保留关联关系不展开
如果需要同时保留两侧的关联标识,不需要完整展开关联对象,可以给主键字段加@XmlID,关联字段加@XmlIDREF,序列化时关联字段只会输出主键值,不会递归展开对象:
@XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "") @XmlRootElement(name = "domain") @Entity class Domain { @Id @XmlID // 标记该字段为对象唯一标识 private String uuid; @XmlIDREF // 序列化时仅输出parent的uuid值,不展开完整对象 @ManyToOne private Domain parent; @XmlIDREF // 序列化时子域列表仅输出每个子域的uuid值 @OneToMany private List<Domain> subdomains; // 其他字段、getter/setter省略 }
- 方案3:集成Jackson序列化器处理循环引用
CXF支持替换默认的JAXB JSON序列化器为Jackson,使用Jackson提供的循环引用处理注解灵活性更高,不需要丢弃任何一侧的关联信息:
- 给Domain类加
@JsonIdentityInfo注解,同一个对象只会完整序列化一次,后续引用仅输出主键:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "uuid") @Entity class Domain { private String uuid; private Domain parent; private List<Domain> subdomains; // 其他字段、getter/setter省略 }
- 配置CXF使用Jackson作为JSON序列化Provider即可生效。
最后修正接口的笔误问题:
@GET @Path("/domains/{uuid}") // 路径变量和@PathParam保持一致 @Produces("application/json") public Domain getDomains(@PathParam("uuid") String uuid){ // 返回值修正为Domain return domainsService.getByUuid(uuid); }
内容的提问来源于stack exchange,提问作者tim08
相关产品推荐
相关产品推荐

