Spring:InvalidDefinitionException: Direct self-reference leading to cycle

一、现象

一个列表接口报错,没有返回信息,异常堆栈如下:

11:52:05.096 [http-nio-8180-exec-36] ERROR c.u.s.f.w.e.GlobalExceptionHandler - [handleRuntimeException,65] - 请求地址'XXXXX',发生未知异常.
org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class XXXX]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Direct self-reference leading to cycle (through reference chain: XXXX.page.TableDataInfo["rows"]->java.util.ArrayList[0]->XXXX["instance"])
        at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:462)
        at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:104)
       ....
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Direct self-reference leading to cycle (through reference chain: XXXX.page.TableDataInfo["rows"]->java.util.ArrayList[0]->XXXX["instance"])
        at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)
        at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1276)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter._handleSelfReference(BeanPropertyWriter.java:944)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:722)
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770)
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
        at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119)
        at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79)
        at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:18)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770)
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
        at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
        at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
        at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1514)
        at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1006)
        at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:456)
        ... 121 common frames omitted

二、解决办法

  1. 不要在可能序列化的Bean内写这种奇怪的方法(这是同事写的方法,至于他写这个方法的原因我下面讲)
	public XXXX getInstance(){
        return this;
    }

或者

	public XXXX getInstance(){
        return new XXXX();
    }
  1. 如果调用的地方不多可以修改方法名,不以get或is开头
  2. 在不需要(不能)序列化的getXXX方法上加@JsonIgnore注解
  3. 好好学习:-(

三、原因分析

spring在使用默认的Jackson对接口返回的数据序列化时出现了直接引用自循环的现象,遂抛出异常。
自循环出现的地方如下:
在这里插入图片描述
看了一下调用这个方法的地方只有一处:

在这里插入图片描述

那么写这个getInstance的原因也就清楚了,为了在toMap的时候将自身作为value。
至此我诚心得为您推荐:

Function.identity()

他的内容很简单:

    /**
     * Returns a function that always returns its input argument.
     *
     * @param <T> the type of the input and output objects to the function
     * @return a function that always returns its input argument
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }

扩展

我们看一下jackson是如何将 getInstance方法当做一个需要序列化的属性的。

1.启动时加载默认配置,认定’get’、‘set’、'is’开头的方法可能需要被序列化

在springboot启动时会使用

DefaultAccessorNamingStrategy$Provider

这个默认配置,设定以’get’、‘set’、'is’为开头的方法可能需要被序列化
在这里插入图片描述
启动进行默认配置时的堆栈:

<init>:286, DefaultAccessorNamingStrategy$Provider (com.fasterxml.jackson.databind.introspect)
<clinit>:370, ObjectMapper (com.fasterxml.jackson.databind)
build:672, Jackson2ObjectMapperBuilder (org.springframework.http.converter.json)
<init>:59, MappingJackson2HttpMessageConverter (org.springframework.http.converter.json)
<init>:91, AllEncompassingFormHttpMessageConverter (org.springframework.http.converter.support)
<init>:215, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
instantiateClass:211, BeanUtils (org.springframework.beans)
instantiate:87, SimpleInstantiationStrategy (org.springframework.beans.factory.support)
instantiateBean:1326, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBeanInstance:1232, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:582, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:542, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:335, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 60187547 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$49)
getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:333, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:208, AbstractBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons:953, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:918, AbstractApplicationContext (org.springframework.context.support)
refresh:583, AbstractApplicationContext (org.springframework.context.support)
<init>:144, ClassPathXmlApplicationContext (org.springframework.context.support)
<init>:85, ClassPathXmlApplicationContext (org.springframework.context.support)
main:32, XXXApplication (com.XXX)

2.controller处理完请求后为返回的POJO查找需要序列化的属性

请求在DispatcherServlet doDispatch中交给controller进行处理,controller处理完后的返回结果经过RequestResponseBodyMethodProcessor的handleReturnValue方法:
在这里插入图片描述
后在AbstractJackson2HttpMessageConverter的writeInternal方法中交给默认的Jackson:ObjectWriter完成POJO的序列化
在这里插入图片描述
继续跟踪,能看到Jackson在实例化前的准备工作,看一下这个方法:POJOPropertiesCollector.getJsonValueAccessor()
在这里插入图片描述
_collected属性被用来控制对一个POJO的解析只进行一次。(这种工作当然只需要做一次就够了)
而在collectAll方法的最后则会缓存解析的结果,并关闭收集开关。
在这里插入图片描述
到这里我们已经离这一小节的目标很近了。看一下collectAll方法里的细节:
在这里插入图片描述
_addFields(props)和_addMethods(props)方法将类中符合序列化条件的属性和方法加入到结果中,随后再进行筛选转换。
在这两个方法中能看到经常用来忽略序列化的实现逻辑(transient关键字和@JsonIgnore注解)
在这里插入图片描述
_addMethods里通过方法参数个数大致将方法分为getter方法(0参数),setter方法(1参数),anySetter(2参数)
这里我们主要关注getter逻辑中调用的_addGetterMethod,它的前半部分主要是对方法进行初步筛选和注解处理
在这里插入图片描述
后半部分则是试图从方法中获取隐藏的属性名,也就是序列化后的key
在这里插入图片描述
其中,_accessorNaming.findNameForRegularGetter(m, m.getName())方法会将我们定义的getInstance方法作为属性进行切割,将instance作为其属性名加入到prop中
在这里插入图片描述

其中_getterPrefix就是第一小节中初始化的默认配置:“get”
解析属性的堆栈:

_addGetterMethod:782, POJOPropertiesCollector (com.fasterxml.jackson.databind.introspect)
_addMethods:687, POJOPropertiesCollector (com.fasterxml.jackson.databind.introspect)
collectAll:422, POJOPropertiesCollector (com.fasterxml.jackson.databind.introspect)
getJsonValueAccessor:270, POJOPropertiesCollector (com.fasterxml.jackson.databind.introspect)
findJsonValueAccessor:258, BasicBeanDescription (com.fasterxml.jackson.databind.introspect)
findSerializerByAnnotations:391, BasicSerializerFactory (com.fasterxml.jackson.databind.ser)
_createSerializer2:220, BeanSerializerFactory (com.fasterxml.jackson.databind.ser)
createSerializer:169, BeanSerializerFactory (com.fasterxml.jackson.databind.ser)
_createUntypedSerializer:1473, SerializerProvider (com.fasterxml.jackson.databind)
_createAndCacheUntypedSerializer:1421, SerializerProvider (com.fasterxml.jackson.databind)
findContentValueSerializer:753, SerializerProvider (com.fasterxml.jackson.databind)
findAndAddSecondarySerializer:90, PropertySerializerMap (com.fasterxml.jackson.databind.ser.impl)
_findAndAddDynamic:311, AsArraySerializerBase (com.fasterxml.jackson.databind.ser.std)
serializeContents:115, IndexedListSerializer (com.fasterxml.jackson.databind.ser.impl)
serialize:79, IndexedListSerializer (com.fasterxml.jackson.databind.ser.impl)
serialize:18, IndexedListSerializer (com.fasterxml.jackson.databind.ser.impl)
serializeFields:808, MapSerializer (com.fasterxml.jackson.databind.ser.std)
serializeWithoutTypeInfo:764, MapSerializer (com.fasterxml.jackson.databind.ser.std)
serialize:720, MapSerializer (com.fasterxml.jackson.databind.ser.std)
serialize:35, MapSerializer (com.fasterxml.jackson.databind.ser.std)
_serialize:480, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser)
serializeValue:400, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser)
serialize:1510, ObjectWriter$Prefetch (com.fasterxml.jackson.databind)
writeValue:1006, ObjectWriter (com.fasterxml.jackson.databind)
writeInternal:456, AbstractJackson2HttpMessageConverter (org.springframework.http.converter.json)
write:104, AbstractGenericHttpMessageConverter (org.springframework.http.converter)
writeWithMessageConverters:290, AbstractMessageConverterMethodProcessor (org.springframework.web.servlet.mvc.method.annotation)
handleReturnValue:183, RequestResponseBodyMethodProcessor (org.springframework.web.servlet.mvc.method.annotation)
handleReturnValue:78, HandlerMethodReturnValueHandlerComposite (org.springframework.web.method.support)
invokeAndHandle:135, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeHandlerMethod:895, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:808, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:87, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch:1067, DispatcherServlet (org.springframework.web.servlet)
doService:963, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doGet:898, FrameworkServlet (org.springframework.web.servlet)
service:645, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:750, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
。。。。。。。。。
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
invoke:687, AbstractAccessLogValve (org.apache.catalina.valves)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1743, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

ps:偶然发现swagger在项目启动的时候也会使用POJOPropertiesCollector.collectAll方法获取类属性

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用和引用中提到了同一个错误信息,即找不到org.springframework.boot:spring-boot-starter-data-redis:pom:unknown这个依赖项。这个错误通常意味着Maven无法从指定的仓库中找到对应的依赖项。根据引用中的提示,这个仓库是nexus-aliyun,链接为http://maven.aliyun.com/nexus/content/groups/public。 为了解决这个问题,你可以尝试以下几个步骤: 1. 确保你的网络连接正常,并且能够访问http://maven.aliyun.com/nexus/content/groups/public这个地址。如果无法访问,可能是由于网络问题或者仓库地址发生了变化,请检查网络设置或者联系仓库管理员。 2. 检查你的Maven配置文件(通常是settings.xml)中是否正确配置了nexus-aliyun仓库。你可以尝试添加或更新以下配置项: ```xml <repositories> <repository> <id>nexus-aliyun</id> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </repository> </repositories> ``` 3. 清理Maven本地仓库并重新构建项目。有时候,Maven可能会将一些错误的缓存数据存储在本地仓库中,导致无法正确下载依赖项。你可以尝试执行以下命令清理本地仓库: ```bash mvn dependency:purge-local-repository ``` 然后重新运行你的构建命令。 希望这些步骤能够帮助你解决该问题。如果问题仍然存在,请提供更多的错误信息和相关配置,以便我们进一步协助你解决。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Could not find artifact org.springframework.boot:spring-boot-starter-parent:jar](https://blog.csdn.net/a1004314120/article/details/128584548)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [解决Could not find artifact org.springframework.boot:spring-boot-maven-plugin:pom](https://blog.csdn.net/weixin_44121790/article/details/116352371)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值