spring详解-循环依赖的解决
<h1 id="spring循环依赖">Spring循环依赖</h1><p><strong>重点提示:</strong> 本文都快写完了,发现“丈夫” 的英文是<strong>husband</strong>....... 在“②有AOP循环依赖” 改过来了,前面用到的位置太多了就没改。我是说怎么idea的hansband英文下面怎么有波浪线。各位能够理解意思就行,英文拼写不要过于在意.</p>
<h2 id="1案例引入">1.案例引入</h2>
<p>在这篇文章中,"②容器刷新"这一小节,留下了如下这样一个疑问。【https://blog.csdn.net/okok__TXF/article/details/147009731】</p>
<pre><code class="language-java">// DefaultSingletonBeanRegistry.java
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/okok__TXF/article/details/147009731
</code></pre>
<p><code>getSingleton(String beanName, boolean allowEarlyReference)</code> 如果allowEarlyReference是true的话,就用<strong>三级缓存来解决循环依赖</strong>。下面以Spring5.3.31再次来追踪一下源码。</p>
<p>本文章示例代码见该仓库:【spring】中的“spring”模块。</p>
<p>仓库地址:https://gitee.com/quercus-sp204/sourcecode-and-demos</p>
<h2 id="2循环依赖分析">2.循环依赖分析</h2>
<h3 id="无aop的循环依赖">①无AOP的循环依赖</h3>
<p>下面举一个例子,一个丈夫和一个妻子的循环依赖。</p>
<pre><code class="language-java">// 这是Main测试类
@Configuration
@ComponentScan("com.feng.myspring")
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
Hansband h = context.getBean(Hansband.class);
h.say();
}
}
//1.丈夫
public class Hansband {
@Autowired
private Wife wife;
public void say() {
System.out.println("我是丈夫");
wife.eat();
}
public Hansband( ) {
}
public void eat() {
System.out.println("丈夫吃饭");
}
}
//2.妻子
public class Wife {
@Autowired
private Hansband hansband;
public void say(){
System.out.println("我是妻子");
hansband.eat();
}
public Wife() {
}
public void eat() {
System.out.println("妻子吃饭");
}
}
</code></pre>
<p>然后再配置类里面创建了一个丈夫和妻子对象</p>
<pre><code class="language-java">@Configuration
public class Config01 {
@Bean
public Hansband hansband(){
return new Hansband();
}
@Bean
public Wife wife(){
return new Wife();
}
}
</code></pre>
<p>然后上面的项目创建好了之后,现在就开始分析如下方法</p>
<pre><code class="language-java">// DefaultSingletonBeanRegistry.java
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
</code></pre>
<p>首先理解一下这其中的变量都是啥。</p>
<ul>
<li>singletonObjects:<strong>单例对象</strong>的缓存,bean名称对应一个bean实例。主要存放的是已经完成实例化、属性填充和初始化所有步骤的单例Bean实例,这样的Bean能够直接提供给用户使用,称之为终态Bean或叫成熟Bean。【一级缓存】</li>
<li>earlySingletonObjects:<strong>早期单例对象</strong>的缓存,bean名称对应一个bean实例。主要存放的已经完成实例化,但属性还没自动赋值的Bean,这些Bean还不能提供用户使用,只是用于提前暴露的Bean实例,把这样的Bean称之为临时Bean或早期的Bean(半成品Bean) 【二级缓存】</li>
<li>singletonFactories:<strong>单例工厂</strong>的缓存,bean名称对应一个对象工厂。存放的是ObjectFactory的匿名内部类实例,调用ObjectFactory.getObject()最终会调用getEarlyBeanReference方法,该方法可以获取提前暴露的单例bean引用。【三级缓存】</li>
</ul>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220158852-1082328899.png" alt="" loading="lazy"></p>
<p>上图是<code>getSingleton(String beanName, boolean allowEarlyReference)</code>方法的大致流程。然后就没了?肯定不是的。</p>
<h4 id="回顾容器刷新">回顾容器刷新</h4>
<p>现在回顾一下容器刷新阶段,里面会调用这样的方法</p>
<pre><code class="language-java">// AbstractApplicationContext.java
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
...
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
// DefaultListableBeanFactory.java
@Override
public void preInstantiateSingletons() throws BeansException {
...
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
...
getBean(beanName); // 会调用这个
...
}
}
...
}
// AbstractBeanFactory.java
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType,
@Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
.......... // ===== 【重要】
}
</code></pre>
<p>上面最后是到了<code>AbstractBeanFactory::doGetBean(xxx)</code>方法了。下面就来着重分析一下这个<code>doGetBean</code>. 这里只挑重点,并不是一行一行地来。</p>
<pre><code class="language-java">// 一、AbstractBeanFactory.java
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
...
// 1.检查单例缓存
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
....
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
//2.原型作用域(prototype) 的 Bean 不支持循环依赖,
//因为每次获取都会创建新实例,Spring 无法通过缓存解决循环依赖。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
....
try {
.....
// 3.Create bean instance.
if (mbd.isSingleton()) {
// 4.调用了重载的getSingleton(xx,xx)方法===
// 第二个参数是lambda表达式
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
....
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
...
}
return adaptBeanInstance(name, beanInstance, requiredType);
}
// 二、DefaultSingletonBeanRegistry.java ----- 重载的getSingleton
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
....
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
.....
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
/* getObject()实际执行的是这个。上面的lambda
() -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
....
}
}
*/
// 实际调用的createBean(beanName, mbd, args);---见下面的三
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
.....
}
.....
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
// 三、AbstractAutowireCapableBeanFactory.java
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
...
try {
// 创建实例bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
...
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
...
}
...
}
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 1,得到bean实例,先从缓存中找,找不到就构造一个
BeanWrapper instanceWrapper = null;
//调用 createBeanInstance 方法根据 Bean 定义和构造函数参数创建一个新的 BeanWrapper 实例。
//从 BeanWrapper 中获取实际的 Bean 实例和 Bean 的类型,并将类型信息记录到 Bean 定义中。
instanceWrapper = createBeanInstance(beanName, mbd, args);
....
// 2.low post-processors to modify the merged bean definition.
// 应用合并后的 Bean 定义后置处理器
synchronized (mbd.postProcessingLock) {
...
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
...
}
//3.提前暴露单例 Bean 以处理循环引用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
....
// 这里是不是就放到了三级缓存中去了,注意一下参数哦。。getEarlyBeanReference
// 将一个 ObjectFactory 放入单例工厂缓存中,该工厂会调用 getEarlyBeanReference 方法
// 该方法会返回一个早期的 Bean 引用,以便在循环依赖时可以提前获取到 Bean 的引用。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
// 4.填充bean属性
populateBean(beanName, mbd, instanceWrapper);
// 5.初始化bean【本文开头的上篇文章】
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
......
}
if (earlySingletonExposure) {
.....
}
...
return exposedObject;
}
</code></pre>
<p><code>doCreateBean</code> 方法是 Spring 框架中用于实际创建 Bean 实例的核心方法。它在 <code>AbstractAutowireCapableBeanFactory</code> 类中实现。该方法承担了创建 Bean 实例、属性填充、初始化以及处理循环依赖等重要任务。从最后一个方法我们可以大致总结三个关于spring创建bean的核心步骤,从上到下按顺序。</p>
<ul>
<li><strong>实例化Bean</strong> : 可以理解为new一个空的bean对象 【<code>createBeanInstance</code>】</li>
<li><strong>填充Bean属性</strong> : 对bean的依赖属性进行填充,对@Value @Autowired @Resource注解标注的属性注入对象引用。【<code>populateBean</code>】</li>
<li><strong>调用Bean初始化方法</strong> : @PostConstruct、init-metohd、等等【<code>initializeBean</code>】</li>
</ul>
<p><strong>这一节我们从最开始的<code>getBean(beanName);</code>,一直往下,发现getBean好像就是创建对象嚯。</strong> 【这句话后面会用到的。。。】</p>
<h4 id="示例分析">示例分析</h4>
<p>那么案例中,我们是字段注入的,肯定就是发生在“填充Bean属性” 这个阶段才会产生的循环依赖问题!!接下来就以上面的“丈夫、妻子”为例子。</p>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220159330-1135545064.png" alt="" loading="lazy"></p>
<p>上图是正在创建Hansband的bean对象、到了属性填充这个步骤了。进入这个populateBean方法里面,</p>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220159765-1856439312.png" alt="" loading="lazy"></p>
<p>上图是populateBean里面,调用这个<code>AutowiredAnnotationBeanPostProcessor :: postProcessProperties(xx)</code>方法。</p>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220200196-1534354042.png" alt="" loading="lazy"></p>
<p>上图中,在这个方法里面,调用 <code>findAutowiringMetadata</code> 方法,根据 Bean 的名称、类型以及属性值集合来查找自动注入的元数据。自动注入元数据包含了 Bean 中需要自动注入的字段、方法等信息,这些信息是通过 <code>@Autowired</code>、<code>@Resource</code> 等注解标记的。可以发现绿色箭头指向的是wife了。随后,调用<code>metadata.inject(bean, beanName, pvs);</code>来执行执行依赖注入操作。</p>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220200569-1727046218.png" alt="" loading="lazy"></p>
<p>上图是<code>metadata.inject</code>里面的内容,对目标对象执行依赖注入操作。for循环遍历注入元素并执行注入操作。<code> element.inject(target, beanName, pvs);</code>。</p>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220200929-443625991.png" alt="" loading="lazy"></p>
<p>上图中,最后如果value不是null,<code>field.set(bean, value)</code>:将解析好的依赖值注入到目标 Bean 的字段中。所以重点应该是上面的<strong><code>resolveFieldValue</code> 核心逻辑</strong></p>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220201381-549118449.png" alt="" loading="lazy"></p>
<p>上图是解析字段的核心逻辑。</p>
<p>首先, 创建依赖描述符(<code>DependencyDescriptor</code>),可以看到将field传了进去,【field很明显就是wife嘛,上图中也可以看出来】。</p>
<p>然后,准备依赖解析环境....这个就不说了。其次,调用了这一行代码<code> beanFactory.resolveDependency(xxx);</code>,这个是Spring 依赖解析的核心方法,根据 <code>DependencyDescriptor</code> 查找匹配的依赖值【可以很明显感觉到,这个又是核心逻辑了】。 找到依赖值后,会缓存起来。</p>
<p>最后会返回这个value嘛。</p>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220201764-1756380034.png" alt="" loading="lazy"></p>
<p>上图是beanFactory.resolveDependency,先会<strong>检查是否需要延迟解析代理(处理 <code>@Lazy</code> 等场景)</strong>,若无需延迟解析,返回 <code>null</code>,进入下一步。然后,<strong>调用核心解析方法 <code>doResolveDependency</code></strong>。</p>
<pre><code class="language-java">// ContextAnnotationAutowireCandidateResolver.java
// 检查是否需要延迟解析
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
}
</code></pre>
<p>接着进入doResolveDependency方法里面看看。</p>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220202163-1610479222.png" alt="" loading="lazy"></p>
<pre><code class="language-java">// DefaultListableBeanFactory.java
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
...
// 一阵噼里啪啦
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
}
// DependencyDescriptor.java
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
throws BeansException {
return beanFactory.getBean(beanName); //
}
</code></pre>
<p>看到这里,明白了没,一切都回来了!!! 前面不是说过吗getBean好像就是创建对象嚯。我们在创建Hansband对象的时候,这个时候并没有创建Wife对象啊,现在兜兜转转又回来了。doGetBean() 现在里面是wife了。!!!!【递归进去,Hansband对象的创建还是卡在populateBean方法的】</p>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220202523-236661662.png" alt="" loading="lazy"></p>
<p>然后Wife又会走一遍上面的流程。我们在最上面创建好Hansband之后,在填充Hansband属性之前,有这样一段代码。</p>
<pre><code class="language-java">//3.提前暴露单例 Bean 以处理循环引用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
....
// 这里是不是就放到了三级缓存中去了,注意一下参数哦。。getEarlyBeanReference
// 将一个 ObjectFactory 放入单例工厂缓存中,该工厂会调用 getEarlyBeanReference 方法
// 该方法会返回一个早期的 Bean 引用,以便在循环依赖时可以提前获取到 Bean 的引用。
// 在后续再缓存中查找Bean时会触发匿名内部类getEarlyBeanReference()方法回调
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
//4. 填充属性
.....
populateBean(beanName, mbd, instanceWrapper);
</code></pre>
<p>创建Wife的时候同理啊,又会调用一遍addSingletonFactory,此时是把wife放到singletonFactories里面了。此时singletonFactories里面就会有两个键值对。</p>
<pre><code class="language-java">hansband -- getEarlyBeanReference("hansband", mbd, bean)
wife -- getEarlyBeanReference("wife", mbd, bean)
</code></pre>
<p>然后就轮到执行Wife的属性填充了,发现需要Hansband的类型的bean,顺着上面梳理的流程,最后又会回到下面这里,只不过此时的参数:descriptor就是hansband,beanName是Wife了。</p>
<pre><code class="language-java">// DefaultListableBeanFactory.java
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
...
// 一阵噼里啪啦
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
// beanFactory.getBean("hansband");递归进去
}
}
</code></pre>
<p>在Wife的属性填充的时候,递归进去拿Hansband对象,会有如下显示【这个时候,三级缓存里面就可以看到有两个键值对了,在这里将Hansband的早期对象拿到,并把它从三级缓存移动到二级缓存中去】</p>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220202868-1832953996.png" alt="" loading="lazy"></p>
<p>这样在Wife属性填充的时候,实现了提前将Hansband曝光给Wife完成属性依赖注入。紧接着,Wife就可以继续完成后面的初始化逻辑,产生一个成熟的Wife。</p>
<p>创建好之后,会<code>addSingleton("wife",xxxx)</code></p>
<pre><code class="language-java">protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject); // 放入一级缓存
this.singletonFactories.remove(beanName); // 移除二三级缓存
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
</code></pre>
<p>请看下面的流程图解释。</p>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220203271-1975674622.png" alt="" loading="lazy"></p>
<p>上图流程中,创建wife的时候,提前注入了一个hansband还没有初始化好的对象【注入了一个早期bean】,这样不会有什么问题吗?</p>
<p>wife创建完全之后,会返回到hansband的“bean初始化阶段”,然后hansband就会初始化ok并放入单例池。由于<strong>wife中的早期bean和 创建hansband中的bean是同一个引用</strong>,故没有啥问题的。</p>
<p>嘶,看了一下,要三级缓存有这个必要吗?图中二级缓存干啥的,getEarlyBeanReference经过调试,发现就是返回了一个bean。看来还是疑点重重,请看下一节。</p>
<h3 id="有aop的循环依赖">②有AOP的循环依赖</h3>
<p>在上面普通bean的循环依赖场景下,可以看出三级缓存貌似并没有什么卵用。【实际上确实是的,在普通的循环依赖的情况下,三级缓存没有任何作用。</p>
<p>】经过反复参考求证,发现三级缓存是和spring 的 AOP挂钩的!</p>
<blockquote>
<p>AOPCSDN地址:https://blog.csdn.net/okok__TXF/article/details/147397816</p>
<p>AOP 博客园地址:https://www.cnblogs.com/jackjavacpp/p/18838920</p>
</blockquote>
<p>看一下上一小节的<code>getEarlyBeanReference(beanName, mbd, bean)</code> 到底做了什么</p>
<pre><code class="language-java">protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
</code></pre>
<p>在未启动AOP代理之前,这个方法中的<code>SmartInstantiationAwareBeanPostProcessor</code>如下所示【AutowiredAnnotationBeanPostProcessor::getEarlyBeanReference()方法就是单纯的返回bean】</p>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220203678-1351807751.png" alt="" loading="lazy"></p>
<p>打开AOP之后,变成了<code>AnnotationAwareAspectJAutoProxyCreator</code>~ 那么它的<code>getEarlyBeanReference()</code>方法有变化吗?</p>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220204027-1881090906.png" alt="" loading="lazy"></p>
<pre><code class="language-java">// AbstractAutoProxyCreator.java
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
/*========== earlyProxyReferences【重要】
跟踪哪些 Bean 的代理对象已在 提前暴露阶段 生成,他的主要作用大概如下
1. 防止重复代理:避免在 Bean 初始化阶段重复创建代理对象,
如果有循环依赖,那么该代理对象在属性填充阶段被创建过了
2. 保证代理对象一致性:确保循环依赖注入的代理对象与最终暴露的代理对象是同一实例
*/
this.earlyProxyReferences.put(cacheKey, bean); // ======
return wrapIfNecessary(bean, beanName, cacheKey); // 下一层
}
// 到这里来了
// 决定是否为给定的 Bean 创建代理对象
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
....
// Create proxy if we have advice.
// getAdvicesAndAdvisorsForBean获取适用于该 Bean 的通知和增强器
// 该方法会根据 Bean 的类型和名称,从 Spring 容器中查找所有匹配的通知和增强器,并返回一个数组
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// DO_NOT_PROXY 就是 null
if (specificInterceptors != DO_NOT_PROXY) { // 不为null
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//调用 createProxy 方法创建代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
</code></pre>
<p>可以大致了解到这个是返回了一个代理对象,可见开启AOP之前和开启AOP之后,果然有不一样。现在我们将案例变成如下有AOP的循环依赖。</p>
<pre><code class="language-java">// 接口
public interface AopMan {
void aopManSay();
}
@Component("husband")
public class AopHusband implements AopMan{
@Autowired
private AopWoman wife;
@Override
public void aopManSay() {
System.out.println("【AOP】Husband say 哦吼");
}
}
// 接口
public interface AopWoman {
void aopWomanSay();
}
@Component("wife")
public class AopWife implements AopWoman{
@Autowired
private AopMan husband;
@Override
public void aopWomanSay() {
System.out.println("【Aop】Wife say 哈哈");
husband.aopManSay();
}
}
// 创建切面
@Component
@Aspect
public class ManAdvice {
private static final String manExpression = "execution(* com.feng.myspring.aopobj.*.aopManSay*(..))";
//环绕通知
@Around(manExpression)
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("##########【环绕通知中的前置通知】##########");
Object returnVale = joinPoint.proceed();
System.out.println("##########【环绕通知中的后置通知】##########");
return returnVale;
}
}
// 主启动测试类
@Configuration
@ComponentScan("com.feng.myspring")
@EnableAspectJAutoProxy(proxyTargetClass = false)
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
// ②有Aop的循环依赖
AopMan husband = context.getBean("husband", AopMan.class);
husband.aopManSay();
System.out.println(husband.getClass()); // 打印一下类型
AopWoman wife = context.getBean("wife", AopWoman.class);
System.out.println(wife.getClass()); // 打印一下类型
}
}
</code></pre>
<p>上述例子中,wife的<code>aopWomanSay()</code>方法里面会调用husband的<code>aopManSay</code>方法,但是此时我是对husband的<code>aopManSay</code>配置了环绕通知的,那么请各位想一下此时wife里面注入的husband是原来的对象吗?肯定不是的,是<strong>代理对象</strong>,注入的是husband的代理对象,可以运行一下Main测试类看看结果【结果如下】</p>
<pre><code class="language-java">【Aop】Wife say 哈哈
##########【环绕通知中的前置通知】##########
【AOP】Husband say 哦吼
##########【环绕通知中的后置通知】##########
class com.sun.proxy.$Proxy18 // husband是一个代理对象
class com.feng.myspring.aopobj.AopWife// wife仍然是原来
</code></pre>
<p>我们都知道springbean的创建过程,是 1.缓存查询;2.创建对象;3.属性填充、注入依赖;4.执行初始化操作;5.这个bean创建好了。大致这样五个过程,在没有循环依赖的前提下,开启aop会生成bean的代理对象,这个生成代理对象的时机是在 “<strong>4.执行初始化操作</strong>” 这一步的,但是本文讨论的都是在第三步哦~~【AOP见下面文章】。所以,三级缓存的存在就是为了提前用对象工厂获取代理对象,并赋值给wife的husband属性【代理对象】依赖注入。</p>
<blockquote>
<p>AOPCSDN地址:https://blog.csdn.net/okok__TXF/article/details/147397816</p>
<p>AOP 博客园地址:https://www.cnblogs.com/jackjavacpp/p/18838920</p>
</blockquote>
<p>回到本小节开始的时候,开启aop之后,<code>AnnotationAwareAspectJAutoProxyCreator</code>通过<code>getEarlyBeanReference</code>里面的<code>wrapIfNecessary</code>拿到的代理实例,我们对husband进行了<code>AOP</code>代理的话,那么此时<code>getEarlyBeanReference</code>将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着wife中注入的husband将是一个代理对象而不是husband的实例化阶段创建后的对象。</p>
<p>Spring是在何处将husband的代理对象放进去的呢?</p>
<p><img src="https://img2023.cnblogs.com/blog/2358057/202505/2358057-20250506220204397-516257098.png" alt="" loading="lazy"></p>
<p>在完成husband初始化后,Spring又调用了一次<code>getSingleton</code>方法,允许早期引用为false。在前面分析的时候在为wife中注入husband时已经将三级缓存中的工厂取出,并从工厂中获取到了一个husband代理对象放入到了二级缓存中,并且从三级缓存中移除掉,所以这里的这个<code>getSingleton</code>方法做的时间就是从二级缓存中获取到这个代理后的husband对象。</p>
<h2 id="3疑问">3.疑问</h2>
<p><strong>1.为什么三级缓存要弄一个对象工厂添加进去,我直接往三级缓存放入对象的代理对象不行吗?</strong></p>
<p>这个工厂的目的在于<strong>延迟对实例化阶段生成的对象的代理</strong>,只有真正发生循环依赖的时候,才去提前生成代理对象,否则只会创建一个工厂并将其放入到三级缓存中,但是不会去通过这个工厂去真正创建对象。</p>
<p>读者在此处思考一下,如果看过AOP,那么肯定就会知道,代理对象的生成是在bean的初始化操作中的<code>后置处理器的postProcessAfterInitialization</code>这一步的,而我们循环依赖发生在属性填充这一步。<strong>发生循环依赖的时候,需要注入代理对象,但是还没到代理对象生成那一步</strong>。</p>
<pre><code class="language-java">try {
// 4.填充bean属性
populateBean(beanName, mbd, instanceWrapper);
// 5.初始化bean【本文开头的上篇文章】
exposedObject = initializeBean(beanName, exposedObject, mbd);
} .....
</code></pre>
<p>在 Spring 框架中,<strong>三级缓存(<code>singletonFactories</code>)存储的是 <code>ObjectFactory</code>,而非直接存储代理对象</strong>,这一设计是为了解决循环依赖中代理对象生成的 <strong>时机问题</strong></p>
<p><strong>2.用二级缓存不行吗?</strong></p>
<p><strong><mark>循环依赖的简单场景(无代理)</mark></strong> : 是没问题的</p>
<p>假设有两个 Bean:<strong>A</strong> 和 <strong>B</strong>,它们互相依赖。Spring 的解决流程如下:</p>
<ol>
<li><strong>创建 A</strong>:实例化 A(调用构造方法),此时 A 还未完成属性填充和初始化。</li>
<li><strong>暴露早期对象</strong>:将 A 的原始对象包装成 <code>ObjectFactory</code>,存入 <strong>三级缓存</strong>(<code>singletonFactories</code>)。</li>
<li><strong>填充 A 的属性</strong>:发现 A 依赖 B,开始创建 B。</li>
<li><strong>创建 B</strong>:实例化 B,填充 B 的属性时发现依赖 A。</li>
<li><strong>从三级缓存获取 A</strong>:通过 <code>ObjectFactory.getObject()</code> 获取 A 的早期引用(原始对象),注入到 B 中。</li>
<li><strong>完成 B 的初始化</strong>:B 初始化完成后,存入一级缓存(<code>singletonObjects</code>)。</li>
<li><strong>完成 A 的初始化</strong>:将 A 的最终对象存入一级缓存,替换三级缓存中的临时对象。</li>
</ol>
<p><strong>若没有代理需求</strong>,二级缓存(<code>earlySingletonObjects</code>)似乎可以直接存储原始对象,无需三级缓存。但问题在于:<strong>当 Bean 需要被代理时,必须确保注入的是代理对象而非原始对象</strong>。</p>
<hr>
<p><strong><mark>循环依赖 + 代理的复杂场景</mark></strong></p>
<p>假设 A 和 B 都需要被 AOP 代理(例如被 <code>@Transactional</code> 标记),此时若仅用二级缓存,会引发以下问题:</p>
<ol>
<li><strong>A 的创建流程</strong>:
<ul>
<li>实例化 A(原始对象)。</li>
<li>将 A 的原始对象存入二级缓存(<code>earlySingletonObjects</code>)。</li>
<li>填充属性时发现依赖 B,开始创建 B。</li>
</ul>
</li>
<li><strong>B 的创建流程</strong>:
<ul>
<li>实例化 B(原始对象)。</li>
<li>填充 B 的属性时,从二级缓存获取 A 的原始对象(未代理)。</li>
<li>完成 B 的初始化后,生成 B 的代理对象,存入一级缓存。</li>
</ul>
</li>
<li><strong>完成 A 的初始化</strong>:
<ul>
<li>在 A 的初始化后阶段(<code>postProcessAfterInitialization</code>),生成 A 的代理对象。</li>
<li>最终缓存中的 A 是代理对象,但 B 中注入的 A 是原始对象,<strong>导致不一致</strong>!</li>
</ul>
</li>
</ol>
<p>说实话,我自己都不能说服我自己。写的好勉强。。。这些疑问还是看这篇文章吧。【参考里面的第二篇文章】</p>
<h2 id="end参考">end.参考</h2>
<ol>
<li>
<p>https://mp.weixin.qq.com/s/dSRQBSG42MYNa992PvtnJA【阿里云开发者 --一文详解Spring Bean循环依赖】</p>
</li>
<li>
<p>https://www.cnblogs.com/daimzh/p/13256413.html 【博客园 面试必杀技,讲一讲Spring中的循环依赖 】质量很高</p>
</li>
<li>
<p>原文链接:https://blog.csdn.net/chaitoudaren/article/details/105060882 【CSDNSpring源码最难问题】</p>
</li>
</ol><br><br>
来源:https://www.cnblogs.com/jackjavacpp/p/18863227
頁:
[1]