Spring Bean的生命周期(一图流+详细说明)

在我的博客阅读本文

1. 前言

Spring是一个IOC(Inversion of Control,控制反转)容器框架,拥有DI(Dependency Injection,依赖注入)DL(Dependency Lookup,依赖查找 等功能。

认为一个IOC框架最基本要有以下功能:

  • 配置解析
  • 对象创建
  • 对象生命周期管理

本文基于Spring-5.2.2.RELEASE版本进行论述,为了简明扼要,在展示部分代码的时候省略了一些细节。

Spring Bean的生命周期总结因为没有一个正式的文档,因此是一个比较偏主观的分享,笔者只能结合小马哥的观点与自身的认识情况大概进行了总结,主要思路还是围绕着方法的调用栈,笔者认为,写在一个方法里,那么就可以认为是一个阶段。

2. Spring Bean生命周期

这里po一张我自己画的图:

Spring%20Bean%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%200e4b70a5ffd54a4bb12cc41b2dfe98d6/Untitled.png

我将SpringBean生命周期分为以下五个阶段:

  1. 注册阶段
  2. 合并阶段
  3. 实例化阶段
  4. 初始化阶段
  5. 销毁阶段

下面具体看一下每个阶段所做的事情。

2.1. 注册阶段

注册阶段的主要任务是通过各种BeanDefinitionReader读取各种配置来源信息(比如读取xml文件、注解等),并将其转化为BeanDefintion的过程。

这里要理解一下BeanDefinition的作用。众所周知,Spring提供了多种多样的注册Bean的方法,BeanDefinition的作用就是去定义并描述一个Spring Bean,方便后续解析实例化等操作。

ApplicationContext#register()方法完成了对象注册阶段,其最终是调用的DefaultListableBeanFactory#registerBeanDefinition() 完成的BeanDefinition注册,这里的“注册”意思是将配置信息转化为BeanDefinition并放到合适的容器中,我们可以看以下代码:

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
   
   

	/** Map of bean definition objects, keyed by bean name. */
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

	/** List of bean definition names, in registration order. */
	private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

	//省略部分代码......
	@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
   
   
				//省略了一些判断代码......
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
	}
}

其中,beanDefinitionMap的数据结构是ConcurrentHashMap,因此不能保证顺序,为了记录注册的顺序,这里使用了ArrayList类型的beanDefinitionNames用来记录注册顺序。

2.2. 合并阶段

经过了注册阶段,Spring的BeanDefinition容器中已经有了部分BeanDefinition信息(可能还存在通过aware接口或者postProcessor接口注册进来的beanDefinition),下面分为两种情况:

  • 对于设置了非懒加载属性的BeanDefinition,在容器启动时(ApplicationContext#refresh())时会最终调用BeanFactory#getBean()方法进行实例化
  • 对于懒加载(isLazyInit)的BeanDefinition,则需要在用到的时候调用BeanFactory#getBean()方法进行实例化。

事实上,无论哪种情况,Spring最终都会调用BeanFactory#getBean()方法进行实例化。在getBean()方法中会有一个合并阶段:

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
   
   
	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
   
   
		//......
		final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
		//......
	}
}

AbstractBeanFactory#getMergedLocalBeanDefinition()方法完成了BeanDefinition的合并,这里的“合并”的意思是,部分BeanDefinition可能不是RootBeanDefinition(没有parent),而是部分属性需要继承其他BeanDefinition,比如xml配置中的parent属性,这就需要进行一次合并,最终产出RootBeanDefinition。

RootBeanDefinition的parent设置时候会有一个判断,可以看出来,RootBeanDefinition最典型的特点就是没有parent reference:

public class RootBeanDefinition extends AbstractBeanDefinition {
   
   
	//......
	@Override
	public void setParentName(@Nullable String parentName) {
   
   
		if (parentName != null) {
   
   
			throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
		}
	}
	//......
}

2.3. 实例化阶段

到了实例化阶段,Spring将转化BeanDefinition中BeanDefinition为实例Bean(放在包装类BeanWrapper中)。

2.3.1. 通过ClassLoader赋值BeanDefinition的beanClass为Class对象

我们首先关注到AbstractAutowireCapableBeanFactory#createBean()方法,AbstractAutowireCapableBeanFactory是DefaultListableBeanFactory的父类,在这个方法中有个过程:

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
      implements AutowireCapableBeanFactory {
   
   
	//......
	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
   
   

		if (logger.isTraceEnabled()) {
   
   
			logger.trace("Creating instance of bean '" + beanName + "'");
		}
		RootBeanDefinition mbdToUse = mbd;

		//......

		// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
		Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
		if (bean != null) {
   
   
				return bean;
		}
		//......

		// Make sure bean class is actually resolved at this point, and
		// clone the bean definition in case of a dynamically resolved Class
		// which cannot be stored in the shared merged bean definition.
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
   
   
			mbdToUse =
<think>嗯,用户问的是归并排序合并阶段左右子数组是否需要有序。首先,我得回忆下归并排序的基本步骤。归并排序是分治法的个典型应用,分为分解和合并两个阶段。分解阶段将数组分成越来越小的子数组,直到每个子数组只有个元素,这时候自然是有序的。合并阶段则是将这些有序的子数组逐步合并成更大的有序数组,直到整个数组有序。 那问题来了,合并的时候左右子数组是否需要已经有序呢?根据归并排序的原理,分解到最后每个子数组都是单个元素,所以合并的初始阶段左右子数组都是有序的。然后每次合并都是将两个有序的子数组合并成个更大的有序数组。所以在合并阶段,左右子数组必须是有序的,这样才能正确地进行合并操作。如果左右子数组无序,合并后的结果也无法保证有序,整个排序过程就会失败。 比如,假设左边是[3,1],右边是[4,2],合并的时候如果直接按顺序比较,结果可能还是无序的。但归并排序在合并之前,已经通过递归分解确保每个子数组都是有序的,所以合并时只需要按顺序比较两个数组的元素即可。这个过程需要两个指针分别指向左右子数组的起始位置,然后选择较小的元素放入结果数组中,直到所有元素都被合并。这也是用户提到的引用中的内容,合并两个有序数组需要这样的步骤[^1][^2]。 总结来说,归并排序的合并阶段必须要求左右子数组都是有序的,这样才能保证合并后的数组也是有序的。如果子数组无序,归并排序就无法正确工作。所以答案应该是肯定的,左右子数组必须有序。</think>归并排序的合并阶段需要左右子数组均为**有序状态**,这是保证合并结果正确性的必要条件。具体分析如下: 1. **分治策略的基础** - 归并排序通过递归将原始数组分解为最小单元(单个元素),此时每个子数组自然有序 - 在合并阶段,算法假设要合并的两个子数组已分别完成排序 2. **合并操作机制** ```python def merge(left, right): result = [] i = j = 0 # 需要左右数组均有序才能正确执行比较操作 while i < len(left) and j < len(right): if left[i] < right[j]: result.append(left[i]) i += 1 else: result.append(right[j]) j += 1 # 合并剩余元素 result.extend(left[i:]) result.extend(right[j:]) return result ``` - 合并过程依赖两个指针遍历已排序的子数组 - 若子数组无序,无法保证合并结果的有序性(例如可能出现$left[i] > left[i+1]$的情况) 3. **算法正确性证明** - 数学归纳法表明:当第$k$层合并完成时,所有长度为$2^k$的子数组必定有序 - 合并操作满足$T(n) = 2T(n/2) + O(n)$的时间复杂度,其成立前提是子数组有序
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值