<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Spring on Aoidayo</title>
        <link>https://blog.aoidayo.site/tags/spring/</link>
        <description>Recent content in Spring on Aoidayo</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-CN</language>
        <lastBuildDate>Sat, 03 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.aoidayo.site/tags/spring/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>SpringBoot2.6&#43;单例bean字段注入的循环依赖问题</title>
            <link>https://blog.aoidayo.site/java/spring/spring-l3cache-qa/</link>
            <pubDate>Sat, 03 Oct 2026 00:00:00 +0000</pubDate>
            <guid>https://blog.aoidayo.site/java/spring/spring-l3cache-qa/</guid>
            <description>&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&#xA;&lt;/h2&gt;&lt;p&gt;看面经的时候顺手测了一下Spring的三级缓存，发现默认配置是无法解决单例Bean通过Setter/字段注入形成的循环依赖问题。研究了一下之后发现：SpringL3Cache对于解决单例模式下setter或字段注入的CycleReference并不总是默认开启的，SpringFrameWork默认开启，但在SpringBoot2.6+的版本中（本地SpringBoot3.0.5）就不默认容许循环依赖，具体来说是&lt;code&gt;allow-circular-references=false&lt;/code&gt;，不允许提前暴露对象到L2~L3Cache中。&lt;/p&gt;&#xA;&lt;p&gt;虽然可以通过&lt;code&gt;spring.main.allow-circular-references: true&lt;/code&gt;来开启，但这也说明SpringBoot官方建议：如果条件允许，coding时尽量不要出现循环依赖。这也是Idea建议使用&lt;code&gt;构造器注入&lt;/code&gt;的原因，提前暴露可能的循环依赖问题。&lt;/p&gt;&#xA;&lt;p&gt;&lt;del&gt;可见循环依赖确实人厌狗嫌。&lt;/del&gt;&lt;/p&gt;&#xA;&lt;p&gt;默认禁用提前暴露三缓。&lt;/p&gt;&#xA;&lt;h2 id=&#34;具体的&#34;&gt;具体的&#xA;&lt;/h2&gt;&lt;p&gt;Spring的三级缓存机制具备解决部分循环依赖的能力，但Spring Boot 2.6+默认禁止Bean之间的循环依赖。&lt;/p&gt;&#xA;&lt;p&gt;Spring能处理的典型情况是：单例Bean之间，通过Setter / 字段注入形成的循环依赖。其核心是在Bean完成初始化之前，通过提前暴露 Bean 引用来打破依赖环。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;strong&gt;SpringBoot2.6默认不允许提前暴露引用&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;从Spring Boot 2.6开始，&lt;code&gt;spring.main.allow-circular-references&lt;/code&gt; 默认值改为&lt;code&gt;false&lt;/code&gt;，因此即使底层仍然存在三级缓存，Spring Boot默认也不会允许通过“提前暴露 Bean”来解决循环依赖。&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;执行问题，报错：默认SpringBoot2.6+ this.allowCircularReferences 为 false 时：创建 Bean 时不允许提前暴露 singleton 到L2~L3Cache。&#xA;1. A 实例化后不会往 singletonFactories（三级缓存）里放 ObjectFactory；&#xA;2. B 注入 A 时调 getSingleton(&amp;#34;test05_A&amp;#34;, true) → L1/L2/L3 全查不到 → 返回 null；&#xA;3. 于是去真正 doGetBean(&amp;#34;test05_A&amp;#34;) → 而 A 已在 singletonsCurrentlyInCreation 里 → beforeSingletonCreation 直接抛 BeanCurrentlyInCreationException。&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果确实需要，可以配置：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;spring:&#xA;  main:&#xA;    allow-circular-references: true&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;重新允许Spring尝试解决循环依赖。官方同时明确建议不要依赖循环引用，而应该从代码设计上消除依赖环。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;strong&gt;构造器注入&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;构造器注入并不能解决循环依赖，反而会让循环依赖直接暴露出来。例如&lt;code&gt;A(B b)&lt;/code&gt;和&lt;code&gt;B(A a)&lt;/code&gt;：创建A必须先有B，创建B又必须先有 A，此时 A 连“原始对象”都还没有实例化出来，自然无法提前暴露。所以构造器循环依赖通常直接启动失败。这也是推荐构造器注入的一个好处：让不合理的依赖关系更早暴露，而不是由三级缓存悄悄兜底。&lt;/p&gt;&#xA;&lt;p&gt;IDEA 推荐构造器注入的原因之一，是构造器注入能够让 Bean 之间的依赖关系更加明确，并且让循环依赖在创建阶段直接暴露，而不是依靠 Spring 的提前引用机制进行兜底；它是帮助发现并避免循环依赖，并非解决循环依赖。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;strong&gt;Spring和SpringBoot的默认行为不同，但都不建议使用循环依赖。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Spring Framework 自身的 AbstractAutowireCapableBeanFactory#allowCircularReferences 默认实际上仍然是 true，而 Spring Boot 2.6+ 在 Boot 层把它配置成了 false。Spring Framework 默认值 和 Spring Boot 默认行为不同。Spring Framework 的 Javadoc 也明确说循环引用会导致某个 Bean 拿到“尚未完全初始化”的另一个 Bean，因此官方建议不要依赖这种机制。&lt;/p&gt;&#xA;</description>
        </item></channel>
</rss>
