Spring源码解读之@Autowired如何解决循环依赖

本文探讨了在Spring框架中,@Autowired注解如何处理循环依赖问题。首先介绍了@Autowired的作用,接着阐述了循环依赖的概念,并通过示例展示了A和B类之间的依赖关系。文章深入解析了Spring解决循环依赖的机制,包括BeanFactory的早绑定回调和ObjectFactory的使用,详细解释了在bean创建过程中如何避免和解决循环依赖,确保正确注入。
部署运行你感兴趣的模型镜像

@Autowired的what&how

在spring框架下,我们可以通过@Autowired注解对属性或者方法参数进行标注,当spring ioc容器初始化时,会帮我们从容器中拿到对应的实例进行注入

什么是循环依赖

假如现在有两个Bean如下所示

public class BeanA {
    @Autowired
    private BeanB beanB;
}

public class BeanB {
    @Autowired
    private BeanA beanA;
}

然后我们通过annotationConfigApplicationContext#register将两个bean的信息注入到容器中,最后通过refresh进行容器到初始化操作

public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
        annotationConfigApplicationContext.register(Bean1.class);
        annotationConfigApplicationContext.register(Bean2.class);
        annotationConfigApplicationContext.refresh();
    }

可以看到A跟B互相依赖,试着想象:当容器先初始化beanA时,必然要对属性beanB进行赋值,这个时候容器中还没有beanB,那么势必会触发beanB的初始化流程,而beanB初始化的完成也需要对属性beanA赋值,但beanA还未初始化完成,这里就产生了所谓的循环依赖。

spring如何解决循环依赖

这里有一个很关键的属性:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	/** Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}

key是beanName,value是一个对象工厂,我们点进去看一下

public interface ObjectFactory<T> {

	T getObject() throws BeansException;

}

其实这里的getObject()就是最终解决循环依赖所调用的方法。
那么程序是怎样执行到这的呢?
我们先从bean的创建入手
如果容器还未实例化bean,那么就会走到这里

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
		BeanWrapper instanceWrapper = null;
		if (instanceWrapper == null) {
			//实例化bean,如果@Autowired加在构造方法上,
			//那么就会在这里完成注入
			//因为下面的回调还未注册,所以这里无法解决循环依赖
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		
		final Object bean = instanceWrapper.getWrappedInstance();
		
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			//往单例工厂(之前说的singletonFactories)中添加一个
			//ObjectFactory的匿名实现作为回调,
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
			
			//属性赋值,处理@Autowired(非构造方法)
			populateBean(beanName, mbd, instanceWrapper);
		}

这里我们发现,在实例化bean跟对属性赋值之间有一个addSingletonFactory的操作,作用是注册一个可以获取当前正在创建的bean的一个回调

	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		synchronized (this.singletonObjects) {
			if (!this.singletonObjects.containsKey(beanName)) {
				this.singletonFactories.put(beanName, singletonFactory);
			}
		}
	}

进入回调,发现回调默认返回的就是bean本身

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}
	
	default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		//	返回bean本身
		return bean;
	}

ok,这里得出一个结论,即使bean未初始化完成,spring也提供了方法来获取这个bean的实例。
如果应用到我们上面的栗子中来就是:

  1. beanA实例化完成
  2. 添加获取beanA的回调到singletonFactories
  3. 调用populateBean,处理@Autowired,注入beanB

因为beanB还未创建,那么势必会进入创建beanB的流程,当beanB也走到populateBean时,也需要完成beanA的注入,这时就会尝试从beanFactory中获取beanA,这里最终会进到
AbstractBeanFactorydoGetBean

	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
	}

这里很关键,进入getSingleton(beanName)

	public Object getSingleton(String beanName) {
		return getSingleton(beanName, true);
	}
	
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					//拿到之前注册的单例工厂对象
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
					    //调用之前注册的回调
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

当beanB走到这里时通过beanAbeanName获取beanA,首先会尝试从singletonObjects中获取,这里肯定获取不到,因为singletonObjectsput操作是在bean初始化完成之后。所以只能通过调用之前注册的回调singletonFactory.getObject()来获取beanA
那么到此beanA注入到beanB的顺利完成,当beanB初始化完成之后,其实beanAgetBean()也就返回了beanB的引用,到此beanA也可以顺利完成依赖注入。

您可能感兴趣的与本文相关的镜像

Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

<think>好的,我现在需要帮助用户理解Spring IOC容器的源码实现和工作原理。用户之前已经提供了一些相关的引用内容,我需要结合这些内容来组织一个清晰且详细的回答。 首先,我应该先回顾用户提供的引用,找出关键点。引用1提到BeanFactory和ApplicationContext是两个核心接口,需要分析它们的源码差异。引用2提到了IoC容器的基本概念、关键接口、bean的生命周期和循环依赖解决方法。引用3提到了事件发布器和监听器的初始化,这部分涉及观察者模式。引用4是关于prepareRefresh方法的预处理步骤,包括初始化属性和早期事件处理。引用5提到了Bean工厂后置处理器和Bean实例化的完成。 接下来,我需要确定回答的结构。通常,源码分析可以从容器启动流程、Bean生命周期管理、核心接口差异、设计模式应用等方面展开。用户的问题比较广泛,所以需要覆盖主要部分,同时保持结构清晰。 第一步,容器启动流程。根据引用4中的prepareRefresh方法,这是刷新前的预处理步骤,包括设置启动时间、状态,初始化属性,验证环境变量,保存早期事件。然后,引用5中的invokeBeanFactoryPostProcessors和finishBeanFactoryInitialization是后续步骤,处理Bean工厂后置处理器和实例化Bean。需要详细说明这些步骤的顺序和作用。 第二步,Bean的生命周期管理。这里需要结合BeanFactory和ApplicationContext的区别,可能涉及Bean的创建、初始化、依赖注入、销毁等阶段。引用2中提到bean的生命周期是一个重点,可能需要详细描述每个阶段涉及的接口和方法,比如BeanPostProcessor。 第三步,关键接口分析。BeanFactory作为基础接口,而ApplicationContext扩展了更多功能,如事件发布、国际化等。引用1和引用2都提到了这两个接口,需要比较它们的源码和功能差异,比如ApplicationContext如何继承BeanFactory并添加额外特性。 第四部分,设计模式的应用。例如,观察者模式在事件处理中的应用,如引用3中的事件发布器和监听器的初始化。可能还有工厂模式在Bean创建中的使用,单例模式在Bean作用域中的应用等。 第五部分,解决循环依赖。引用2提到循环依赖是高级特性,需要解释Spring如何通过三级缓存解决这个问题,可能涉及源码中的DefaultSingletonBeanRegistry类中的getSingleton方法及相关处理。 在组织内容时,要确保每个部分都有对应的源码引用和解释,例如在容器启动流程中,可以引用prepareRefresh的代码片段,说明其作用。同时,结合用户提供的引用,正确标注引用标识,如[^4]、[^5]等。 另外,用户可能需要示例代码或流程图来辅助理解,但根据问题描述,用户要求的是文本回答,所以需要用清晰的步骤和子标题来划分内容。比如使用“1. 容器启动流程”、“2. Bean生命周期管理”等。 最后,生成相关问题,可能需要围绕源码分析、设计模式、具体实现细节等方面提问,例如询问BeanFactory和ApplicationContext的区别,或者如何扩展BeanPostProcessor接口。 需要确保回答符合用户的要求:结构清晰,分步骤解释,引用正确,并且使用中文。同时,避免使用Markdown格式,但根据用户提供的示例,可能需要使用代码块来展示源码片段,如引用4中的prepareRefresh方法代码。 现在需要整合这些思路,形成一个连贯的回答,涵盖用户提到的所有关键点,并正确引用提供的参考文献。</think>### Spring IOC 源码分析及详细解读 #### 一、容器启动流程 Spring IOC 容器的启动流程围绕 `ApplicationContext` 的 `refresh()` 方法展开,核心步骤如下: 1. **预处理阶段 (`prepareRefresh()`)** - 记录启动时间,设置容器状态为激活。 - 初始化环境变量和配置参数(如 `initPropertySources()`)。 - 验证必要属性是否合法(如 `validateRequiredProperties()`)。 - 准备早期事件存储队列 `earlyApplicationEvents` [^4]。 2. **加载Bean定义 (`obtainFreshBeanFactory()`)** - 创建 `BeanFactory`(默认实现为 `DefaultListableBeanFactory`)。 - 解析 XML 或注解配置,将 Bean 定义注册到 `BeanDefinitionMap` 中。 3. **处理后置处理器 (`invokeBeanFactoryPostProcessors()`)** - 调用 `BeanFactoryPostProcessor` 实现类(如 `ConfigurationClassPostProcessor`),完成配置类解析、`@Bean` 方法解析等[^5]。 4. **初始化事件和监听器** - 通过 `initApplicationEventMulticaster()` 初始化事件广播器。 - 通过 `registerListeners()` 注册监听器,支持观察者模式[^3]。 5. **实例化单例Bean (`finishBeanFactoryInitialization()`)** - 遍历 `BeanDefinitionMap`,对非懒加载的单例 Bean 进行实例化、依赖注入和初始化[^5]。 --- #### 二、Bean生命周期管理 Bean 的生命周期由以下接口控制: ```java // 示例代码:Bean生命周期关键接口 public interface BeanPostProcessor { Object postProcessBeforeInitialization(Object bean, String beanName); Object postProcessAfterInitialization(Object bean, String beanName); } ``` 1. **实例化**:通过反射调用构造函数。 2. **属性注入**:通过 `populateBean()` 方法注入依赖(如 `@Autowired`)。 3. **初始化**: - 调用 `InitializingBean.afterPropertiesSet()`。 - 执行自定义初始化方法(如 `@PostConstruct`)。 4. **销毁**:容器关闭时调用 `DisposableBean.destroy()` 或自定义销毁方法。 --- #### 三、核心接口对比:BeanFactory vs ApplicationContext | 特性 | BeanFactory | ApplicationContext | |---------------------|---------------------------|----------------------------------------| | Bean实例化 | 延迟加载 | 支持预加载(通过 `refresh()` 触发) | | 国际化支持 | 无 | 有(`MessageSource`) | | 事件发布 | 无 | 有(`ApplicationEventPublisher`) | | AOP集成 | 需手动配置 | 自动集成 | | 资源访问 | 基础支持 | 扩展支持(如 `ResourceLoader`) | ApplicationContext 通过组合模式扩展了 BeanFactory 的功能[^1]。 --- #### 四、设计模式应用 1. **工厂模式**:`BeanFactory` 是工厂接口,`DefaultListableBeanFactory` 是具体实现。 2. **观察者模式**:事件广播器 (`ApplicationEventMulticaster`) 和监听器 (`ApplicationListener`) 实现解耦[^3]。 3. **模板方法模式**:`AbstractApplicationContext.refresh()` 定义了容器刷新的步骤骨架。 --- #### 五、循环依赖解决 Spring 通过**三级缓存**解决循环依赖: 1. **一级缓存** (`singletonObjects`):存放完全初始化的 Bean。 2. **二级缓存** (`earlySingletonObjects`):存放未完成属性注入的早期 Bean 引用。 3. **三级缓存** (`singletonFactories`):存放 Bean 工厂对象,用于生成早期代理对象。 源码关键方法: ```java // DefaultSingletonBeanRegistry 类 protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; } ``` ---
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值