微服务间调用导致的Could not write content: Infinite recursion (StackOverflowError)问题

在微服务架构中,通过Feign进行服务间调用时遇到Infinite recursion问题,导致StackOverflowError异常。本地调用正常,问题出现在远程调用时,可能与JSON序列化相关。需要检查实体类是否存在循环引用的情况,或者配置Feign客户端的序列化方式以避免无限递归。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在开发中遇到了一个奇葩的问题,在本地调用没有任何问题,只要是通过feign调用就出现递归调用,异常信息如下:

2017-11-01 17:23:10.302 ERROR 4172 --- [nio-6006-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Infinite recursion (StackOverflowError) (through reference chain: org.hibernate.collection.internal.PersistentSet[0]->com.chhliu.srd.rdcloud.user.entity.SysPermission["roleList"]->org.hibernate.collection.internal.PersistentSet[0]->com.chhliu.srd.rdcloud.user.entity.SysRole["permissionList"]->org.hibernate.collection.internal.PersistentSet[0]->com.chhliu.srd.rdcloud.user.entity.SysPermission["roleList"]->org.hibernate.collection.internal.PersistentSet[0] with root cause

java.lang.StackOverflowError: null
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(Unknown Source)
	at java.security.SecureClassLoader.defineClass(Unknown Source)
	at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access$100(Unknown Source)
	at java.net.URLClassLoader$1.run(Unknown Source)
	at java.net.URLClassLoader$1.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:709)
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
	at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:149)
	at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:112)
	at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:25)
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:704)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:690)
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)

从上面的异常信息可以看出,权限表和角色表出现了递归调用,并且最终导致了StackOverflowError的错误。从异常中可以看出,是由于feign在序列化的时候,使用的是jackson,jackson在序列化的时候,导致了递归调用。

角色表如下:

@Entity
@Table(name = "t_sys_role")
@Setter
@Getter
@NoArgsConstructor
@ToString(exclude = {"permissionList", "userList"})
public class SysRole implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * 角色编号
	 */
	@Id
	@GeneratedValue
	@Column(name = "role_id")
	private Long roleId;

	/**
	 * 角色标识 程序中判断使用,如"admin",这个是唯一的:
	 */
	@Column(name = "role_name")
	private String roleName;

	/**
	 * 角色描述,UI界面显示使用
	 */
	@Column(name = "description")
	private String description;

	/**
	 * 是否可用,如果不可用将不会添加给用户
	 */
	@Column(name = "available")
	private Boolean available = Boolean.FALSE;

	/**
	 * 角色 -权限关系: 多对多关系;
	 */
	@ManyToMany(fetch = FetchType.EAGER)
	@JoinTable(name = "t_sys_role_permission", joinColumns = {
			@JoinColumn(name = "roleId") }, inverseJoinColumns = {
					@JoinColumn(name = "permissionId") })
	private Set<SysPermission> permissionList;

	/**
	 * 用户 - 角色关系定义;
	 */
	@JsonBackReference  // 解决循环调用的问题
	@ManyToMany
	@JoinTable(name = "t_sys_user_role", joinColumns = {
			@JoinColumn(name = "roleId")}, inverseJoinColumns = { @JoinColumn(name = "uid") })
	private Set<Employee> userList;
}

权限表如下:

@Entity
@Table(name = "t_sys_permission")
@Setter
@Getter
@NoArgsConstructor
@ToString(exclude = "roleList")
public class SysPermission implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 主键
	 */
	@Id
	@GeneratedValue
	@Column(name = "permission_id")
	private long permissionId;
	
	/**
	 * 权限编码名称
	 */
	@Column(name = "permission_code")
	private String permissionCode;

	/**
	 * 名称
	 */
	@Column(name = "permission_name")
	private String permissionName;

	/**
	 * 一级菜单
	 */
	@Column(name = "first_menu")
	private String firstMenu;

	/**
	 * 二级菜单
	 */
	@Column(name = "second_menu")
	private String secondMenu;

	/**
	 * 资源路径
	 */
	@Column(name = "url")
	private String url;

	/**
	 * 权限字符串,menu例子:role:*,button例子:role:create,role:update,role:delete,role:view
	 */
	@Column(name = "permission")
	private String permission;

	/**
	 * 是否可用,如果不可用将不会添加给用户
	 */
	@Column(name = "available")
	private Boolean available = Boolean.FALSE;

	/**
	 * 权限- 角色关系定义 一个权限对应多个角色
	 */
        @JsonBackReference // 解决循环调用的问题
 @ManyToMany@JoinTable(name = "t_sys_role_permission", joinColumns = {@JoinColumn(name = "permissionId") }, inverseJoinColumns = { @JoinColumn(name = "roleId") })private Set<SysRole> roleList;}

解决方案如下:
比如角色和权限是ManyToMany的关系,那么就在权限对应的角色的setter上加上@JsonBackReference注解,来解决问题,例如上例中在roleList属性上加了该注解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值