最近在开发中遇到了一个奇葩的问题,在本地调用没有任何问题,只要是通过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属性上加了该注解。