https://blog.csdn.net/wufagang/article/details/123027193

发布时间 2023-03-27 11:49:00作者: edda_huang

<div class="RichText ztext Post-RichText css-1g0fqss" options="[object Object]"><p data-first-child="" data-pid="6in8Lo1T">Spring 循环依赖一般包含 构造器注入循环依赖 和字段注入(setter方式)循环依赖, 字段注入循环依赖,Spring 官方通过三层缓存解决。而今天分享的重点是:Spring 是如何解决构造器注入产生的循环依赖问题?</p><blockquote data-pid="5Q0VwEPX">申明:本文源码 基于 springboot-2.7.0 、spring-5.3.20 和 JDK11</blockquote><h2 id="h_562691467_0" data-into-catalog-status="">起因</h2><p data-pid="wYcSOdzQ">前段时间,因部门同事遇到一个 Spring 循环依赖的问题,IDEA 错误信息如下:</p><div class="highlight"><pre><code class="language-text">  ***************************
  APPLICATION FAILED TO START
  ***************************

  Description:

  The dependencies of some of the beans in the application context form a cycle:

  ┌─────┐
  |  orderService defined in file [./target/classes/cn/xxx/spring/OrderService.class]
  ↑     ↓
  |  userService defined in file [./target/classes/cn/xxx/spring/UserService.class]
  └─────┘

  Action:

  Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
</code></pre></div><p data-pid="YhbMPa_B">​错误信息大体意思是:不鼓励依赖循环引用,默认情况下是禁止的。可以通过修改代码,删除 bean 之间的依赖循环。或者通过将 spring.main.allow-circular-references 设置为 true 来自动中断循环。</p><p data-pid="Tq7lMy5E">鉴于自己曾经也遇到过这个问题,因此把曾经整理的云笔记结合源码输出此文,希望帮助到同样遇坑的小伙伴。</p><h2 id="h_562691467_1" data-into-catalog-status="">什么是循环依赖</h2><p data-pid="s4__kBL4">循环依赖是指:对象实例之间依赖关系构成一个环形,分为:单个对象的自我循环、两个对象的相互循环、多个对象的相互循坏。抽象图如下:</p><figure data-size="normal"><noscript><img src="https://pic4.zhimg.com/v2-4cb4f642419ac65ba453c4fc4b72e0ff_b.jpg" data-caption="" data-size="normal" data-rawwidth="859" data-rawheight="287" class="origin_image zh-lightbox-thumb" width="859" data-original="https://pic4.zhimg.com/v2-4cb4f642419ac65ba453c4fc4b72e0ff_r.jpg"/></noscript><div><img src="https://pic4.zhimg.com/80/v2-4cb4f642419ac65ba453c4fc4b72e0ff_720w.webp" data-caption="" data-size="normal" data-rawwidth="859" data-rawheight="287" class="origin_image zh-lightbox-thumb lazy" width="859" data-original="https://pic4.zhimg.com/v2-4cb4f642419ac65ba453c4fc4b72e0ff_r.jpg" data-actualsrc="https://pic4.zhimg.com/v2-4cb4f642419ac65ba453c4fc4b72e0ff_b.jpg" height="287" data-lazy-status="ok"></div></figure><h3 id="h_562691467_2" data-into-catalog-status=""><b><i>单个对象的自我依赖</i></b></h3><div class="highlight"><pre><code class="language-java"><span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">OrderService</span> <span class="o">{</span>
 
    <span class="nd">@Autowired</span>
    <span class="kd">private</span> <span class="n">OrderService</span> <span class="n">orderService</span><span class="o">;</span>
<span class="o">}</span></code></pre></div><p data-pid="bSoAV4SY">这种循环产生的概率很低,自己依赖自己,一般是在代码编写错误的情况下出现,而且很容易发现。</p><p class="ztext-empty-paragraph"><br></p><h3 id="h_562691467_3" data-into-catalog-status="">两个对象的相互循环</h3><p data-pid="Xp49LsAe">从上文 OrderService 和 UserService 两个类的代码可以看出,在初始化 OrderService 类时,需要依赖 UserService,而 UserService 类未实例化,因此需要实例化 UserService 类,但是在初始化 UserService 类时 发现它又依赖 OrderService 类,因此就产生了循环依赖,依赖关系可以抽象成下图:</p><p data-pid="a4R-JcQP">从上文 OrderService 和 UserService 两个类的代码可以看出,在初始化 OrderService 类时,需要依赖 UserService,而 UserService 类未实例化,因此需要实例化 UserService 类,但是在初始化 UserService 类时 发现它又依赖 OrderService 类,因此就产生了循环依赖,依赖关系可以抽象成下图:</p><div class="highlight"><pre><code class="language-java"><span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">OrderService</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="n">UserService</span> <span class="n">userService</span><span class="o">;</span>
    <span class="kd">public</span> <span class="nf">OrderService</span><span class="o">(</span><span class="n">UserService</span> <span class="n">userService</span><span class="o">){</span>
        <span class="k">this</span><span class="o">.</span><span class="na">userService</span> <span class="o">=</span> <span class="n">userService</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="n">User</span> <span class="nf">getUser</span><span class="o">(){</span>
        <span class="k">return</span> <span class="n">userService</span><span class="o">.</span><span class="na">getUser</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">UserService</span> <span class="o">{</span>

  <span class="kd">private</span> <span class="kd">final</span> <span class="n">OrderService</span> <span class="n">orderService</span><span class="o">;</span>
  <span class="kd">public</span> <span class="nf">UserService</span><span class="o">(</span><span class="n">OrderService</span> <span class="n">orderService</span><span class="o">){</span>
    <span class="k">this</span><span class="o">.</span><span class="na">orderService</span> <span class="o">=</span> <span class="n">orderService</span><span class="o">;</span>
  <span class="o">}</span>

  <span class="kd">public</span> <span class="n">Order</span> <span class="nf">getOrder</span><span class="o">(){</span>
    <span class="k">return</span> <span class="n">orderService</span><span class="o">.</span><span class="na">getOrder</span><span class="o">();</span>
  <span class="o">}</span>
<span class="o">}</span></code></pre></div><h3 id="h_562691467_4" data-into-catalog-status=""><b><i>多个对象的依赖成环:</i></b></h3><div class="highlight"><pre><code class="language-java"><span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">OrderService</span> <span class="o">{</span>
  
    <span class="kd">private</span> <span class="kd">final</span> <span class="n">UserService</span> <span class="n">userService</span><span class="o">;</span>
  
    <span class="kd">public</span> <span class="nf">OrderService</span><span class="o">(</span><span class="n">UserService</span> <span class="n">userService</span><span class="o">){</span>
        <span class="k">this</span><span class="o">.</span><span class="na">userService</span> <span class="o">=</span> <span class="n">userService</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="n">User</span> <span class="nf">getUser</span><span class="o">(){</span>
        <span class="k">return</span> <span class="n">userService</span><span class="o">.</span><span class="na">getUser</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">UserService</span> <span class="o">{</span>

  <span class="kd">private</span> <span class="kd">final</span> <span class="n">GoodsService</span> <span class="n">goodsService</span><span class="o">;</span>
  
  <span class="kd">public</span> <span class="nf">UserService</span><span class="o">(</span><span class="n">GoodsService</span> <span class="n">goodsService</span><span class="o">){</span>
    <span class="k">this</span><span class="o">.</span><span class="na">goodsService</span> <span class="o">=</span> <span class="n">goodsService</span><span class="o">;</span>
  <span class="o">}</span>

  <span class="kd">public</span> <span class="n">Goods</span> <span class="nf">getGoodsService</span><span class="o">(){</span>
    <span class="k">return</span> <span class="n">goodsService</span><span class="o">.</span><span class="na">getGoods</span><span class="o">();</span>
  <span class="o">}</span>
<span class="o">}</span>

<span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">GoodsService</span> <span class="o">{</span>

  <span class="kd">private</span> <span class="kd">final</span> <span class="n">OrderService</span> <span class="n">orderService</span><span class="o">;</span>
  
  <span class="kd">public</span> <span class="nf">GoodsService</span><span class="o">(</span><span class="n">OrderService</span> <span class="n">orderService</span><span class="o">){</span>
    <span class="k">this</span><span class="o">.</span><span class="na">orderService</span> <span class="o">=</span> <span class="n">orderService</span><span class="o">;</span>
  <span class="o">}</span>

  <span class="kd">public</span> <span class="n">Order</span> <span class="nf">getOrder</span><span class="o">(){</span>
    <span class="k">return</span> <span class="n">orderService</span><span class="o">.</span><span class="na">getOrder</span><span class="o">();</span>
  <span class="o">}</span>
<span class="o">}</span></code></pre></div><p data-pid="UmcyBEK4">这种循环依赖比较隐蔽,多个对象依赖,最终成环。</p><h2 id="h_562691467_5" data-into-catalog-status="">如何解决循环依赖</h2><h3 id="h_562691467_6" data-into-catalog-status=""><b>1. 修改代码</b></h3><p data-pid="_1WhgcMH">既然循环依赖是代码编写带来的,最彻底的方案是把出现循环依赖的代码重构,但是,重构代码的范围可能不可控,因此,对于测试等存在一定的回归成本,这是一种代价稍微大点的方案。</p><p data-pid="K-i8I76m">不过,代码出现循环依赖,在一定意义上(不是绝对哦)预示了 code smell:为什么会存在循环依赖?代码抽象是否合理?代码设计是否违背了 SOLID 原则?</p><h3 id="h_562691467_7" data-into-catalog-status=""><b>2. 使用字段依赖注入</b></h3><p data-pid="vmjhituO">曾经很长一段时间(Spring 3.0 以前的版本),字段依赖是比较主流的一种编程方式,因为这种方式编写方便简洁,而且 Spring 也利用三层缓存解决了循环依赖问题,但后面因 Spring 不推荐字段依赖注入方式,并且在 github上也可以发现大部分的开源软件也不采用这种方式了,所以该方案也仅供参考不推荐,改造代码如下:</p><div class="highlight"><pre><code class="language-java"><span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">OrderService</span> <span class="o">{</span>
    <span class="nd">@Autowired</span>
    <span class="kd">private</span> <span class="n">UserService</span> <span class="n">userService</span><span class="o">;</span>

    <span class="kd">public</span> <span class="n">User</span> <span class="nf">getUser</span><span class="o">(){</span>
        <span class="k">return</span> <span class="n">userService</span><span class="o">.</span><span class="na">getUser</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">UserService</span> <span class="o">{</span>
  <span class="nd">@Autowired</span>
  <span class="kd">private</span> <span class="n">OrderService</span> <span class="n">orderService</span><span class="o">;</span>

  <span class="kd">public</span> <span class="n">Order</span> <span class="nf">getOrder</span><span class="o">(){</span>
    <span class="k">return</span> <span class="n">orderService</span><span class="o">.</span><span class="na">getOrder</span><span class="o">();</span>
  <span class="o">}</span>
<span class="o">}</span></code></pre></div><h3 id="h_562691467_8" data-into-catalog-status=""><b>3. 使用 @Lazy 注解</b></h3><p data-pid="9_BWewZ-">@Lazy 是 spring 3.0 提供的一个注解,用来表示是否要延迟初始化 bean,首先看下 @Lazy 注解的源码:</p><div class="highlight"><pre><code class="language-java"><span class="cm">/**
</span><span class="cm"> * Indicates whether a bean is to be lazily initialized.
</span><span class="cm"> *
</span><span class="cm"> * &lt;p&gt;May be used on any class directly or indirectly annotated with {@link
</span><span class="cm"> * org.springframework.stereotype.Component @Component} or on methods annotated with
</span><span class="cm"> * {@link Bean @Bean}.
</span><span class="cm"> *
</span><span class="cm"> * &lt;p&gt;If this annotation is not present on a {@code @Component} or {@code @Bean} definition,
</span><span class="cm"> * eager initialization will occur. If present and set to {@code true}, the {@code @Bean} or
</span><span class="cm"> * {@code @Component} will not be initialized until referenced by another bean or explicitly
</span><span class="cm"> * retrieved from the enclosing {@link org.springframework.beans.factory.BeanFactory
</span><span class="cm"> * BeanFactory}. If present and set to {@code false}, the bean will be instantiated on
</span><span class="cm"> * startup by bean factories that perform eager initialization of singletons.
</span><span class="cm"> *
</span><span class="cm"> * &lt;p&gt;If Lazy is present on a {@link Configuration @Configuration} class, this
</span><span class="cm"> * indicates that all {@code @Bean} methods within that {@code @Configuration}
</span><span class="cm"> * should be lazily initialized. If {@code @Lazy} is present and false on a {@code @Bean}
</span><span class="cm"> * method within a {@code @Lazy}-annotated {@code @Configuration} class, this indicates
</span><span class="cm"> * overriding the 'default lazy' behavior and that the bean should be eagerly initialized.
</span><span class="cm"> *
</span><span class="cm"> * &lt;p&gt;In addition to its role for component initialization, this annotation may also be placed
</span><span class="cm"> * on injection points marked with {@link org.springframework.beans.factory.annotation.Autowired}
</span><span class="cm"> * or {@link javax.inject.Inject}: In that context, it leads to the creation of a
</span><span class="cm"> * lazy-resolution proxy for all affected dependencies, as an alternative to using
</span><span class="cm"> * {@link org.springframework.beans.factory.ObjectFactory} or {@link javax.inject.Provider}.
</span><span class="cm"> * Please note that such a lazy-resolution proxy will always be injected; if the target
</span><span class="cm"> * dependency does not exist, you will only be able to find out through an exception on
</span><span class="cm"> * invocation. As a consequence, such an injection point results in unintuitive behavior
</span><span class="cm"> * for optional dependencies. For a programmatic equivalent, allowing for lazy references
</span><span class="cm"> * with more sophistication, consider {@link org.springframework.beans.factory.ObjectProvider}.
</span><span class="cm"> *
</span><span class="cm"> * @author Chris Beams
</span><span class="cm"> * @author Juergen Hoeller
</span><span class="cm"> * @since 3.0
</span><span class="cm"> * @see Primary
</span><span class="cm"> * @see Bean
</span><span class="cm"> * @see Configuration
</span><span class="cm"> * @see org.springframework.stereotype.Component
</span><span class="cm"> */</span>
<span class="nd">@Target</span><span class="o">({</span><span class="n">ElementType</span><span class="o">.</span><span class="na">TYPE</span><span class="o">,</span> <span class="n">ElementType</span><span class="o">.</span><span class="na">METHOD</span><span class="o">,</span> <span class="n">ElementType</span><span class="o">.</span><span class="na">CONSTRUCTOR</span><span class="o">,</span> <span class="n">ElementType</span><span class="o">.</span><span class="na">PARAMETER</span><span class="o">,</span> <span class="n">ElementType</span><span class="o">.</span><span class="na">FIELD</span><span class="o">})</span>
<span class="nd">@Retention</span><span class="o">(</span><span class="n">RetentionPolicy</span><span class="o">.</span><span class="na">RUNTIME</span><span class="o">)</span>
<span class="nd">@Documented</span>
<span class="kd">public</span> <span class="nd">@interface</span> <span class="n">Lazy</span> <span class="o">{</span>

    <span class="cm">/**
</span><span class="cm">     * Whether lazy initialization should occur.
</span><span class="cm">     */</span>
    <span class="kt">boolean</span> <span class="nf">value</span><span class="o">()</span> <span class="k">default</span> <span class="kc">true</span><span class="o">;</span>

<span class="o">}</span></code></pre></div><p data-pid="dMF6GBht">从 @Lazy 注解的源码可以总结几点:</p><ol><li data-pid="0zcHFmNl">@Lazy 用来标识类是否需要延迟加载;</li><li data-pid="_QbCVGAK">@Lazy 可以作用在类上、方法上、构造器上、方法参数上、成员变量中;</li><li data-pid="rFyi4GUk">@Lazy 作用于类上时,通常与 @Component 及其衍生注解配合使用;</li><li data-pid="CUVXMOpP">@Lazy 注解作用于方法上时,通常与 @Bean 注解配合使用;</li></ol><p data-pid="O9OuroFM">因此,通过 @Lazy 解决构造器循环依赖的代码改造如下:</p><div class="highlight"><pre><code class="language-java"><span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">UserService</span> <span class="o">{</span>

  <span class="kd">private</span> <span class="kd">final</span> <span class="n">OrderService</span> <span class="n">orderService</span><span class="o">;</span>

  <span class="nd">@Lazy</span>
  <span class="kd">public</span> <span class="nf">UserService</span><span class="o">(</span><span class="n">OrderService</span> <span class="n">orderService</span><span class="o">){</span>
    <span class="k">this</span><span class="o">.</span><span class="na">orderService</span> <span class="o">=</span> <span class="n">orderService</span><span class="o">;</span>
  <span class="o">}</span>
  <span class="c1">// 或者
</span><span class="c1"></span>  <span class="kd">public</span> <span class="nf">UserService</span><span class="o">(</span><span class="nd">@Lazy</span> <span class="n">OrderService</span> <span class="n">orderService</span><span class="o">){</span>
    <span class="k">this</span><span class="o">.</span><span class="na">orderService</span> <span class="o">=</span> <span class="n">orderService</span><span class="o">;</span>
  <span class="o">}</span>

  <span class="kd">public</span> <span class="n">Order</span> <span class="nf">getOrder</span><span class="o">(){</span>
    <span class="k">return</span> <span class="n">orderService</span><span class="o">.</span><span class="na">getOrder</span><span class="o">();</span>
  <span class="o">}</span>
<span class="o">}</span></code></pre></div><h2 id="h_562691467_9" data-into-catalog-status="">@Lazy 原理剖析</h2><p data-pid="Zwgku8UV">本文使用的是 Springboot-2.7.0 启动,因此整体思路是:Springboot是如何启动 Spring IOC容器?如何加载 Bean?如何 处理 @Lazy注解?</p><p data-pid="vCWgh3YV">源码查看足迹可以参考下面的类:</p><blockquote data-pid="_OFxuokK">Springboot 启动类 main() 调用 org.springframework.boot.SpringApplication#run()<br>org.springframework.boot.SpringApplication#refreshContext()<br>org.springframework.context.support.AbstractApplicationContext#refresh()<br>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean()<br>org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties()<br>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean<br>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean<br>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance<br>org.springframework.beans.factory.support.ConstructorResolver#autowireConstructor<br>org.springframework.beans.factory.support.ConstructorResolver#resolvePreparedArguments<br>org.springframework.beans.factory.support.ConstructorResolver#resolveAutowiredArgument<br>org.springframework.beans.factory.config.AutowireCapableBeanFactory#resolveDependency()</blockquote><p data-pid="ppDSoocS">这里摘取了处理构造器依赖的几个核心方法来解释@Lazy 如何解决循环依赖</p><p data-pid="XhASDqn2">UserService构造器注入OrderService是强依赖关系,因此会经过AbstractAutowireCapableBeanFactory#createBeanInstance()中关于构造器逻辑代码:</p><div class="highlight"><pre><code class="language-java"><span class="c1">// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
</span><span class="c1"></span><span class="kd">class</span> <span class="nc">AbstractAutowireCapableBeanFactory</span><span class="o">{</span>
    <span class="c1">// Create a new instance for the specified bean, using an appropriate instantiation strategy: factory method, constructor autowiring, or simple instantiation.
</span><span class="c1"></span>    <span class="c1">// 使用适当的实例化策略为指定的 bean 创建一个新实例:工厂方法、构造函数自动装配或简单实例化。
</span><span class="c1"></span>    <span class="kd">protected</span> <span class="n">BeanWrapper</span> <span class="nf">createBeanInstance</span><span class="o">(</span><span class="n">String</span> <span class="n">beanName</span><span class="o">,</span> <span class="n">RootBeanDefinition</span> <span class="n">mbd</span><span class="o">,</span> <span class="nd">@Nullable</span> <span class="n">Object</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
      <span class="c1">// Candidate constructors for autowiring?
</span><span class="c1"></span>      <span class="n">Constructor</span><span class="o">&lt;?&gt;[]</span> <span class="n">ctors</span> <span class="o">=</span> <span class="n">determineConstructorsFromBeanPostProcessors</span><span class="o">(</span><span class="n">beanClass</span><span class="o">,</span> <span class="n">beanName</span><span class="o">);</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">ctors</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">mbd</span><span class="o">.</span><span class="na">getResolvedAutowireMode</span><span class="o">()</span> <span class="o">==</span> <span class="n">AUTOWIRE_CONSTRUCTOR</span> <span class="o">||</span>
          <span class="n">mbd</span><span class="o">.</span><span class="na">hasConstructorArgumentValues</span><span class="o">()</span> <span class="o">||</span> <span class="o">!</span><span class="n">ObjectUtils</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">(</span><span class="n">args</span><span class="o">))</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">autowireConstructor</span><span class="o">(</span><span class="n">beanName</span><span class="o">,</span> <span class="n">mbd</span><span class="o">,</span> <span class="n">ctors</span><span class="o">,</span> <span class="n">args</span><span class="o">);</span>
      <span class="o">}</span>
        <span class="o">}</span>
<span class="o">}</span>
</code></pre></div><p data-pid="g9xQBxKI">在 autowireConstructor(beanName, mbd, ctors, args) 方法会调用 ConstructorResolver#resolvePreparedArguments(),再进入ConstructorResolver#resolveAutowiredArgument(), 再进入DefaultListableBeanFactory#resolveDependency(), resolveDependency()方法的 getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary 逻辑就是针对Lazy情况进行处理: 判断构造器参数是有@Lazy注解,有则通过buildLazyResolutionProxy 生成代理对象,无则直接返回beanName。而在buildLazyResolutionProxy()里会生成 一个TargetSource对象来和代理对象相关联。部分源码如下:</p><div class="highlight"><pre><code class="language-java"><span class="c1">// org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
</span><span class="c1"></span><span class="kd">public</span> <span class="kd">class</span> <span class="nc">DefaultListableBeanFactory</span><span class="o">{</span>
  <span class="kd">public</span> <span class="n">Object</span> <span class="nf">resolveDependency</span><span class="o">(</span><span class="n">DependencyDescriptor</span> <span class="n">descriptor</span><span class="o">,</span> <span class="nd">@Nullable</span> <span class="n">String</span> <span class="n">requestingBeanName</span><span class="o">,</span>
                                  <span class="nd">@Nullable</span> <span class="n">Set</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="n">autowiredBeanNames</span><span class="o">,</span> <span class="nd">@Nullable</span> <span class="n">TypeConverter</span> <span class="n">typeConverter</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">BeansException</span> <span class="o">{</span>

    <span class="c1">// 此处省略部分代码
</span><span class="c1"></span>    <span class="k">if</span> <span class="o">(</span><span class="n">Optional</span><span class="o">.</span><span class="na">class</span> <span class="o">==</span> <span class="n">descriptor</span><span class="o">.</span><span class="na">getDependencyType</span><span class="o">())</span> <span class="o">{</span>
    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
       <span class="c1">// 处理 Lazy 逻辑
</span><span class="c1"></span>      <span class="n">Object</span> <span class="n">result</span> <span class="o">=</span> <span class="n">getAutowireCandidateResolver</span><span class="o">().</span><span class="na">getLazyResolutionProxyIfNecessary</span><span class="o">(</span>
        <span class="n">descriptor</span><span class="o">,</span> <span class="n">requestingBeanName</span><span class="o">);</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">result</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">doResolveDependency</span><span class="o">(</span><span class="n">descriptor</span><span class="o">,</span> <span class="n">requestingBeanName</span><span class="o">,</span> <span class="n">autowiredBeanNames</span><span class="o">,</span> <span class="n">typeConverter</span><span class="o">);</span>
      <span class="o">}</span>
      <span class="k">return</span> <span class="n">result</span><span class="o">;</span>
    <span class="o">}</span>
  <span class="o">}</span>
<span class="o">}</span>

<span class="c1">// org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessary
</span><span class="c1"></span><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ContextAnnotationAutowireCandidateResolver</span> <span class="kd">extends</span> <span class="n">QualifierAnnotationAutowireCandidateResolver</span> <span class="o">{</span>

    <span class="nd">@Override</span>
    <span class="nd">@Nullable</span>
    <span class="kd">public</span> <span class="n">Object</span> <span class="nf">getLazyResolutionProxyIfNecessary</span><span class="o">(</span><span class="n">DependencyDescriptor</span> <span class="n">descriptor</span><span class="o">,</span> <span class="nd">@Nullable</span> <span class="n">String</span> <span class="n">beanName</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">// 判断注解是否有@Lazy,有则通过buildLazyResolutionProxy 生成代理对象,没有则直接返回beanName
</span><span class="c1"></span>        <span class="k">return</span> <span class="o">(</span><span class="n">isLazy</span><span class="o">(</span><span class="n">descriptor</span><span class="o">)</span> <span class="o">?</span> <span class="n">buildLazyResolutionProxy</span><span class="o">(</span><span class="n">descriptor</span><span class="o">,</span> <span class="n">beanName</span><span class="o">)</span> <span class="o">:</span> <span class="kc">null</span><span class="o">);</span>
    <span class="o">}</span>

  <span class="kd">protected</span> <span class="kt">boolean</span> <span class="nf">isLazy</span><span class="o">(</span><span class="n">DependencyDescriptor</span> <span class="n">descriptor</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">for</span> <span class="o">(</span><span class="n">Annotation</span> <span class="n">ann</span> <span class="o">:</span> <span class="n">descriptor</span><span class="o">.</span><span class="na">getAnnotations</span><span class="o">())</span> <span class="o">{</span>
      <span class="n">Lazy</span> <span class="n">lazy</span> <span class="o">=</span> <span class="n">AnnotationUtils</span><span class="o">.</span><span class="na">getAnnotation</span><span class="o">(</span><span class="n">ann</span><span class="o">,</span> <span class="n">Lazy</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">lazy</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">lazy</span><span class="o">.</span><span class="na">value</span><span class="o">())</span> <span class="o">{</span>
        <span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
      <span class="o">}</span>
    <span class="o">}</span>
    <span class="n">MethodParameter</span> <span class="n">methodParam</span> <span class="o">=</span> <span class="n">descriptor</span><span class="o">.</span><span class="na">getMethodParameter</span><span class="o">();</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">methodParam</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">Method</span> <span class="n">method</span> <span class="o">=</span> <span class="n">methodParam</span><span class="o">.</span><span class="na">getMethod</span><span class="o">();</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">method</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="kt">void</span><span class="o">.</span><span class="na">class</span> <span class="o">==</span> <span class="n">method</span><span class="o">.</span><span class="na">getReturnType</span><span class="o">())</span> <span class="o">{</span>
        <span class="n">Lazy</span> <span class="n">lazy</span> <span class="o">=</span> <span class="n">AnnotationUtils</span><span class="o">.</span><span class="na">getAnnotation</span><span class="o">(</span><span class="n">methodParam</span><span class="o">.</span><span class="na">getAnnotatedElement</span><span class="o">(),</span> <span class="n">Lazy</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">lazy</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">lazy</span><span class="o">.</span><span class="na">value</span><span class="o">())</span> <span class="o">{</span>
          <span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
        <span class="o">}</span>
      <span class="o">}</span>
    <span class="o">}</span>
    <span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
  <span class="o">}</span>
<span class="o">}</span></code></pre></div><p data-pid="hwVhhY42">通过上面核心代码的解读,我们可以知道,构造器(参数)增加 @Lazy 注解后,Spring不会去初始化参数对应类的实例,而是返回它的一个代理对象,解决了循环依赖问题,逻辑可以抽象为下图:</p><p class="ztext-empty-paragraph"><br></p><figure data-size="normal"><noscript><img src="https://pic2.zhimg.com/v2-c6ac30da54f240734adf911830ae4b85_b.jpg" data-caption="" data-size="normal" data-rawwidth="834" data-rawheight="288" class="origin_image zh-lightbox-thumb" width="834" data-original="https://pic2.zhimg.com/v2-c6ac30da54f240734adf911830ae4b85_r.jpg"/></noscript><div><img src="https://pic2.zhimg.com/80/v2-c6ac30da54f240734adf911830ae4b85_720w.webp" data-caption="" data-size="normal" data-rawwidth="834" data-rawheight="288" class="origin_image zh-lightbox-thumb lazy" width="834" data-original="https://pic2.zhimg.com/v2-c6ac30da54f240734adf911830ae4b85_r.jpg" data-actualsrc="https://pic2.zhimg.com/v2-c6ac30da54f240734adf911830ae4b85_b.jpg" height="288" data-lazy-status="ok"></div></figure><p class="ztext-empty-paragraph"><br></p><p data-pid="ldrDTnhT">尽管循环依赖的问题解决了,但是,UserService类 依赖的只是OrderService的一个代理对象。因此,我们自然会好奇:当调用orderService.getOrder()时,spring是如何找到 OrderService 的真实对象呢?</p><p data-pid="LAjOAg7A">从上文知道,注入给UserService类的是一个代理,说起代理就不得不说起Spring AOP机制,它就是通过动态代理实现的(JDK动态代理 和 CGLib动态代理)。 因为OrderService并非接口,因此不能使用 JDK动态代理,只能通过 CGLib进行代理,CGLib源码如下:</p><div class="highlight"><pre><code class="language-java"><span class="c1">// org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept
</span><span class="c1"></span><span class="kd">class</span> <span class="nc">CglibAopProxy</span> <span class="kd">implements</span> <span class="n">AopProxy</span><span class="o">,</span> <span class="n">Serializable</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">DynamicAdvisedInterceptor</span> <span class="kd">implements</span> <span class="n">MethodInterceptor</span><span class="o">,</span> <span class="n">Serializable</span> <span class="o">{</span>
      <span class="kd">public</span> <span class="n">Object</span> <span class="nf">intercept</span><span class="o">(</span><span class="n">Object</span> <span class="n">proxy</span><span class="o">,</span> <span class="n">Method</span> <span class="n">method</span><span class="o">,</span> <span class="n">Object</span><span class="o">[]</span> <span class="n">args</span><span class="o">,</span> <span class="n">MethodProxy</span> <span class="n">methodProxy</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Throwable</span> <span class="o">{</span>
        <span class="n">Object</span> <span class="n">oldProxy</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
        <span class="kt">boolean</span> <span class="n">setProxyContext</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
        <span class="n">Object</span> <span class="n">target</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
        <span class="n">TargetSource</span> <span class="n">targetSource</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">advised</span><span class="o">.</span><span class="na">getTargetSource</span><span class="o">();</span>
        <span class="k">try</span> <span class="o">{</span>
          <span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">advised</span><span class="o">.</span><span class="na">exposeProxy</span><span class="o">)</span> <span class="o">{</span>
            <span class="c1">// Make invocation available if necessary.
</span><span class="c1"></span>            <span class="n">oldProxy</span> <span class="o">=</span> <span class="n">AopContext</span><span class="o">.</span><span class="na">setCurrentProxy</span><span class="o">(</span><span class="n">proxy</span><span class="o">);</span>
            <span class="n">setProxyContext</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
          <span class="o">}</span>
          <span class="c1">// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
</span><span class="c1"></span>          <span class="c1">// 获取被代理的对象
</span><span class="c1"></span>          <span class="n">target</span> <span class="o">=</span> <span class="n">targetSource</span><span class="o">.</span><span class="na">getTarget</span><span class="o">();</span>
          <span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">targetClass</span> <span class="o">=</span> <span class="o">(</span><span class="n">target</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">?</span> <span class="n">target</span><span class="o">.</span><span class="na">getClass</span><span class="o">()</span> <span class="o">:</span> <span class="kc">null</span><span class="o">);</span>
          <span class="n">List</span><span class="o">&lt;</span><span class="n">Object</span><span class="o">&gt;</span> <span class="n">chain</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">advised</span><span class="o">.</span><span class="na">getInterceptorsAndDynamicInterceptionAdvice</span><span class="o">(</span><span class="n">method</span><span class="o">,</span> <span class="n">targetClass</span><span class="o">);</span>
          <span class="n">Object</span> <span class="n">retVal</span><span class="o">;</span>
          <span class="c1">// Check whether we only have one InvokerInterceptor: that is,
</span><span class="c1"></span>          <span class="c1">// no real advice, but just reflective invocation of the target.
</span><span class="c1"></span>          <span class="k">if</span> <span class="o">(</span><span class="n">chain</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">()</span> <span class="o">&amp;&amp;</span> <span class="n">CglibMethodInvocation</span><span class="o">.</span><span class="na">isMethodProxyCompatible</span><span class="o">(</span><span class="n">method</span><span class="o">))</span> <span class="o">{</span>
            <span class="c1">// We can skip creating a MethodInvocation: just invoke the target directly.
</span><span class="c1"></span>            <span class="c1">// Note that the final invoker must be an InvokerInterceptor, so we know
</span><span class="c1"></span>            <span class="c1">// it does nothing but a reflective operation on the target, and no hot
</span><span class="c1"></span>            <span class="c1">// swapping or fancy proxying.
</span><span class="c1"></span>            <span class="n">Object</span><span class="o">[]</span> <span class="n">argsToUse</span> <span class="o">=</span> <span class="n">AopProxyUtils</span><span class="o">.</span><span class="na">adaptArgumentsIfNecessary</span><span class="o">(</span><span class="n">method</span><span class="o">,</span> <span class="n">args</span><span class="o">);</span>
            <span class="c1">// 通过反射调用被代理对象的方法
</span><span class="c1"></span>            <span class="n">retVal</span> <span class="o">=</span> <span class="n">invokeMethod</span><span class="o">(</span><span class="n">target</span><span class="o">,</span> <span class="n">method</span><span class="o">,</span> <span class="n">argsToUse</span><span class="o">,</span> <span class="n">methodProxy</span><span class="o">);</span>
          <span class="o">}</span>
          <span class="k">else</span> <span class="o">{</span>
            <span class="c1">// We need to create a method invocation...
</span><span class="c1"></span>            <span class="n">retVal</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CglibMethodInvocation</span><span class="o">(</span><span class="n">proxy</span><span class="o">,</span> <span class="n">target</span><span class="o">,</span> <span class="n">method</span><span class="o">,</span> <span class="n">args</span><span class="o">,</span> <span class="n">targetClass</span><span class="o">,</span> <span class="n">chain</span><span class="o">,</span> <span class="n">methodProxy</span><span class="o">).</span><span class="na">proceed</span><span class="o">();</span>
          <span class="o">}</span>
          <span class="n">retVal</span> <span class="o">=</span> <span class="n">processReturnType</span><span class="o">(</span><span class="n">proxy</span><span class="o">,</span> <span class="n">target</span><span class="o">,</span> <span class="n">method</span><span class="o">,</span> <span class="n">retVal</span><span class="o">);</span>
          <span class="k">return</span> <span class="n">retVal</span><span class="o">;</span>
        <span class="o">}</span><span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">e</span><span class="o">){}</span>
      <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></div><p data-pid="-UaBKMcA">这里抽取了CGLib动态代理核心的3步:</p><div class="highlight"><pre><code class="language-java"><span class="c1">// 此处的TargetSource 和 上文 buildLazyResolutionProxy() 构建的TargetSource 关联
</span><span class="c1"></span><span class="n">1</span><span class="o">.</span> <span class="n">TargetSource</span> <span class="n">targetSource</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">advised</span><span class="o">.</span><span class="na">getTargetSource</span><span class="o">();</span>

<span class="c1">// 获取被代理的对象target
</span><span class="c1"></span><span class="n">2</span><span class="o">.</span> <span class="n">target</span> <span class="o">=</span> <span class="n">targetSource</span><span class="o">.</span><span class="na">getTarget</span><span class="o">();</span>

<span class="c1">// 反射调用被代理对象的方法
</span><span class="c1"></span><span class="n">3</span><span class="o">.</span> <span class="n">retVal</span> <span class="o">=</span> <span class="n">invokeMethod</span><span class="o">(</span><span class="n">target</span><span class="o">,</span> <span class="n">method</span><span class="o">,</span> <span class="n">argsToUse</span><span class="o">,</span> <span class="n">methodProxy</span><span class="o">);</span></code></pre></div><p data-pid="NDwvjUSw">通过CGLib核心的3步解释了,Spring中代理类是如何与真实对象进行关联,因此,orderService关联到真实对象可以抽象成下图:</p><p class="ztext-empty-paragraph"><br></p><figure data-size="normal"><noscript><img src="https://pic1.zhimg.com/v2-89e7fc35f965eb642e930e3f825208c0_b.jpg" data-caption="" data-size="normal" data-rawwidth="858" data-rawheight="428" class="origin_image zh-lightbox-thumb" width="858" data-original="https://pic1.zhimg.com/v2-89e7fc35f965eb642e930e3f825208c0_r.jpg"/></noscript><div><img src="https://pic1.zhimg.com/80/v2-89e7fc35f965eb642e930e3f825208c0_720w.webp" data-caption="" data-size="normal" data-rawwidth="858" data-rawheight="428" class="origin_image zh-lightbox-thumb lazy" width="858" data-original="https://pic1.zhimg.com/v2-89e7fc35f965eb642e930e3f825208c0_r.jpg" data-actualsrc="https://pic1.zhimg.com/v2-89e7fc35f965eb642e930e3f825208c0_b.jpg" height="428" data-lazy-status="ok"></div></figure><p data-pid="5lczhTEA">另外,我们通过3张 IDEA debugger 截图来佐证下:</p><figure data-size="normal"><noscript><img src="https://pic1.zhimg.com/v2-71a1e31e81505a4dc2231a069caaefd4_b.jpg" data-caption="" data-size="normal" data-rawwidth="1060" data-rawheight="682" class="origin_image zh-lightbox-thumb" width="1060" data-original="https://pic1.zhimg.com/v2-71a1e31e81505a4dc2231a069caaefd4_r.jpg"/></noscript><div><img src="https://pic1.zhimg.com/80/v2-71a1e31e81505a4dc2231a069caaefd4_720w.webp" data-caption="" data-size="normal" data-rawwidth="1060" data-rawheight="682" class="origin_image zh-lightbox-thumb lazy" width="1060" data-original="https://pic1.zhimg.com/v2-71a1e31e81505a4dc2231a069caaefd4_r.jpg" data-actualsrc="https://pic1.zhimg.com/v2-71a1e31e81505a4dc2231a069caaefd4_b.jpg" height="682" data-lazy-status="ok"></div></figure><figure data-size="normal"><noscript><img src="https://pic1.zhimg.com/v2-10b76ebf68071e09b3b7814432e53e0c_b.jpg" data-caption="" data-size="normal" data-rawwidth="1101" data-rawheight="842" class="origin_image zh-lightbox-thumb" width="1101" data-original="https://pic1.zhimg.com/v2-10b76ebf68071e09b3b7814432e53e0c_r.jpg"/></noscript><div><img src="https://pic1.zhimg.com/80/v2-10b76ebf68071e09b3b7814432e53e0c_720w.webp" data-caption="" data-size="normal" data-rawwidth="1101" data-rawheight="842" class="origin_image zh-lightbox-thumb lazy" width="1101" data-original="https://pic1.zhimg.com/v2-10b76ebf68071e09b3b7814432e53e0c_r.jpg" data-actualsrc="https://pic1.zhimg.com/v2-10b76ebf68071e09b3b7814432e53e0c_b.jpg" height="842" data-lazy-status="ok"></div></figure><figure data-size="normal"><noscript><img src="https://pic2.zhimg.com/v2-78e882f30274e1bc55060b2d3bfc8395_b.jpg" data-caption="" data-size="normal" data-rawwidth="1080" data-rawheight="825" class="origin_image zh-lightbox-thumb" width="1080" data-original="https://pic2.zhimg.com/v2-78e882f30274e1bc55060b2d3bfc8395_r.jpg"/></noscript><div><img src="https://pic2.zhimg.com/80/v2-78e882f30274e1bc55060b2d3bfc8395_720w.webp" data-caption="" data-size="normal" data-rawwidth="1080" data-rawheight="825" class="origin_image zh-lightbox-thumb lazy" width="1080" data-original="https://pic2.zhimg.com/v2-78e882f30274e1bc55060b2d3bfc8395_r.jpg" data-actualsrc="https://pic2.zhimg.com/v2-78e882f30274e1bc55060b2d3bfc8395_b.jpg" height="825" data-lazy-status="ok"></div></figure><h2 id="h_562691467_10" data-into-catalog-status="">总结</h2><ul><li data-pid="SH4kZ8RU">Spring构造器注入循环依赖有3种解决办法:重构代码、字段依赖注入、@Lazy注解。强烈推荐 @Lazy注解</li><li data-pid="x4hca212">@Lazy注解 解决思路是:初始化时注入代理对象,真实调用时使用Spring AOP动态代理去关联真实对象,然后通过反射完成调用。</li><li data-pid="JNL-QNS4">@Lazy注解 加在构造器上,作用域为构造器所有参数,加在构造器某个参数上,作用域为该参数</li><li data-pid="xUDhGOvW">@Lazy注解 作用在接口上,使用 JDK动态代理,作用在类上,使用 CGLib动态代理</li></ul></div>