You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Jersey将POJO转为JSON时出现栈溢出问题求助

解决多对多关系下JSON序列化栈溢出问题

这个问题我之前也碰到过,核心原因就是多对多关系带来的循环引用导致序列化时无限递归,最终触发栈溢出。XML能正常工作是因为JAXB(JAX-RS默认的XML提供者)识别了你在Group类的users字段上加的@XmlTransient注解,序列化时会跳过反向引用;但JSON提供者要么不识别JAXB注解,要么配置没到位,所以没处理好循环引用。

下面给你几个可行的解决方案:

方案1:切换到MOXy作为JSON提供者(推荐,原生支持JAXB注解)

MOXy是EclipseLink提供的JAXB扩展,原生支持JAXB注解,而且能直接处理JSON序列化。GlassFish 5自带MOXy,但默认可能没启用它作为JSON提供者,你需要手动注册:

方式1:通过JAX-RS应用类注册

创建一个继承Application的类,注册MOXyJsonProvider:

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.persistence.jaxrs.MOXyJsonProvider;

@ApplicationPath("/api") // 替换成你的API根路径
public class MyJaxrsApplication extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> classes = new HashSet<>();
        // 注册MOXy JSON提供者
        classes.add(MOXyJsonProvider.class);
        // 注册你的REST资源类(比如User相关的资源)
        classes.add(UserResource.class);
        return classes;
    }
}

方式2:通过web.xml配置

如果你的项目用web.xml管理JAX-RS,可以添加以下配置:

<servlet>
    <servlet-name>JAX-RS Servlet</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>org.eclipse.persistence.jaxrs.MOXyJsonProvider</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>JAX-RS Servlet</servlet-name>
    <url-pattern>/api/*</url-pattern> <!-- 替换成你的API路径 -->
</servlet-mapping>

注册后,MOXy会识别你已有的@XmlTransient注解,跳过循环引用的反向关联,JSON序列化就不会栈溢出了。

方案2:让默认的Jackson提供者识别JAXB注解

GlassFish 5默认的JSON提供者是Jackson,它本身不识别JAXB注解,需要添加Jackson的JAXB模块来支持:

步骤1:添加依赖

在你的父POM里添加Jackson JAXB模块的依赖:

<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-jaxb-annotations</artifactId>
    <version>2.9.8</version> <!-- 版本尽量匹配GlassFish 5自带的Jackson版本 -->
    <scope>provided</scope> <!-- 如果GlassFish已包含该模块,用provided;否则用compile -->
</dependency>

步骤2:注册JAXB模块到Jackson

创建一个ContextResolver来配置Jackson的ObjectMapper,注册JAXB注解模块:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
public class JacksonObjectMapperProvider implements ContextResolver<ObjectMapper> {
    private final ObjectMapper objectMapper;

    public JacksonObjectMapperProvider() {
        objectMapper = new ObjectMapper();
        // 注册JAXB注解模块,让Jackson识别@XmlTransient等注解
        objectMapper.registerModule(new JaxbAnnotationModule());
        // 可选:关闭循环引用检测失败的报错,避免栈溢出
        objectMapper.configure(com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_CIRCULAR_REFERENCES, false);
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return objectMapper;
    }
}

最后把这个Provider注册到你的JAX-RS应用类里(或者通过web.xml配置),Jackson就能识别你的@XmlTransient注解了。

方案3:直接使用Jackson的循环引用注解

如果你不想依赖JAXB注解,可以直接用Jackson的专属注解来处理循环引用:

  • 在User类的groups字段上添加@JsonManagedReference(标记正向引用)
  • 在Group类的users字段上添加@JsonBackReference(标记反向引用,序列化时会被忽略)

修改后的实体类代码:

// User类
@Entity 
@XmlRootElement 
public class User extends Person { 
    @Column(length = 60) 
    private String email; 
    @Column(name = "PSEUDO", length = 50) 
    protected String pseudo; 
    
    @ManyToMany(fetch = FetchType.LAZY ,targetEntity = Group.class) 
    @OrderBy("group_name ASC")
    @com.fasterxml.jackson.annotation.JsonManagedReference
    protected List<ItGroup> groups = new LinkedList<ItGroup>(); 
    
    // getters/setters
}

// Group类
@Entity 
@Table(name = "groups") 
public class Group implements ItGroup, Serializable { 
    // ...其他字段
    
    @XmlTransient
    @ManyToMany(fetch = FetchType.LAZY,mappedBy = "groups",targetEntity = User.class) 
    @OrderBy("email ASC")
    @com.fasterxml.jackson.annotation.JsonBackReference
    private List<ItUser> users = new LinkedList<ItUser>(); 
    
    // getters/setters
}

这样Jackson序列化时会自动跳过反向引用的users字段,避免无限递归。

额外注意:懒加载问题

你的关联关系用了FetchType.LAZY,如果序列化时Hibernate的Session已经关闭,可能会触发懒加载异常。可以在JSON提供者里配置忽略未加载的懒加载对象:

  • 对于MOXy:可以设置eclipselink.json.include-unmapped为false,或者在实体类上加@XmlTransient到Hibernate的代理字段(比如hibernateLazyInitializer)
  • 对于Jackson:添加@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})到实体类上,或者配置objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)

内容的提问来源于stack exchange,提问作者kaizokun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:37:13