Spring5 源码阅读笔记(1.4.4)多例的实例化

本文深入探讨Spring框架中多例Bean的实例化过程,解释了@Scope(“prototype”)注解的作用,以及多例与单例的区别。通过源码分析,揭示了多例Bean在工厂初始化时的行为特点,以及其懒加载的特性。

在此之前的文章都讲的是单例的实例化。本节讲的是多例的实例化。

什么是多例?
多例就是每次 getBean 操作得到的不是同一个对象。

我们在一个类上打上 @Component 注解后,还可以再打上一个 @Scope(“prototype”),使这个类在实例化的时候是多例的。

Scope 即作用域,除了单例和多例以外,还有其它的模式。它们最大的区别是,如何管理实例对象。单例是在完成工厂初始化的时候就将所有的 Bean 实例放进了缓存,使我们拿到的都是相同的实例,省去了反复创建和销毁的麻烦。而多例在工厂初始化的时候并没有创建,也没有放实例的缓存,所以我们每次拿到的是不同的对象。

以下是 doGetBean 方法中的一段:

else if (mbd.isPrototype()) {
			// It's a prototype -> create a new instance.
			Object prototypeInstance = null;
			try {
				//1.4.4.1
				beforePrototypeCreation(beanName);
				//这个方法和单例的是一样的,见1.4.2
				prototypeInstance = createBean(beanName, mbd, args);
			}
			finally {
				//1.4.4.2
				afterPrototypeCreation(beanName);
			}
			//该方法是FactoryBean接口的调用入口
			bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
		}

完整的代码见Spring5 源码阅读笔记(1.4)finishBeanFactoryInitialization(beanFactory) 完成Bean工厂初始化

在这那一节中,我们知道如果是非抽象的且是单例的且非懒加载的 Bean,Spring 会在完成Bean工厂初始化这个方法里,将它初始化。但多例的并不会。所以在后来的 doGetBean 方法里我们才看到跟多例有关的代码。

这说明了只有当我们手动 getBean 才会进行多例的实例化。可以理解为多例是“懒加载”的。

跟源码

1.4.4.1 beforePrototypeCreation

类 AbstractBeanFactory

protected void beforePrototypeCreation(String beanName) {
	//先从表示正在创建的容器里拿,这里的容器是一个ThreadLocal
	Object curVal = this.prototypesCurrentlyInCreation.get();
	if (curVal == null) {
		//如果容器里原本没有,直接放入
		this.prototypesCurrentlyInCreation.set(beanName);
	}
	//如果容器里已经有一个beanName了,由于ThreadLocal只能存放一个值,
	//就把原来的和要新添加的打包成一个set放入容器
	else if (curVal instanceof String) {
		Set<String> beanNameSet = new HashSet<>(2);
		beanNameSet.add((String) curVal);
		beanNameSet.add(beanName);
		this.prototypesCurrentlyInCreation.set(beanNameSet);
	}
	else {
		//如果容器里本来就是一个set,就把变量放入set
		Set<String> beanNameSet = (Set<String>) curVal;
		beanNameSet.add(beanName);
	}
}

跟 prototypesCurrentlyInCreation:

在这里插入图片描述
使用 ThreadLocal 可以让多个线程彼此创建实例不受干扰。

1.4.4.2 afterPrototypeCreation

类 AbstractBeanFactory

protected void afterPrototypeCreation(String beanName) {
	Object curVal = this.prototypesCurrentlyInCreation.get();
	if (curVal instanceof String) {
		//从表示正在创建的容器移除
		this.prototypesCurrentlyInCreation.remove();
	}
	else if (curVal instanceof Set) {
		Set<String> beanNameSet = (Set<String>) curVal;
		beanNameSet.remove(beanName);
		if (beanNameSet.isEmpty()) {
			this.prototypesCurrentlyInCreation.remove();
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值