Spring源码IOC之创建Bean的前置工作

2.3 创建bean的实例–前置准备工作

Spring Bean的作用域有:

  • prototype
  • singleton
  • request
  • session
  • global session
  • 专栏主要针对 singleton 分析

接上文,Spring 首先尝试从缓存中获取bean的实例,若未能获取到,则创建新的bean的实例:

// Create bean instance.
// 创建bean实例
if (mbd.isSingleton()) {
	sharedInstance = getSingleton(beanName, () -> {
		try {
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			destroySingleton(beanName);
			throw ex;
		}
	});
	beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

方法大体分为四个步骤,内部逻辑十分复杂,但是需要现有整体上的了解:

  • getSingleton 获取单例bean实例
  • createBean 创建bean实例
  • catch 异常处理
  • getObjectForBeanInstance(涉及FactoryBean,后续分析)
2.3.1 getSingleton

抛开创建bean实例来看,该方法主要处理创建bean的前置、后置、缓存等工作。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(beanName, "Bean name must not be null");
	// 加锁
	synchronized (this.singletonObjects) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null) {
			if (this.singletonsCurrentlyInDestruction) {
				throw new BeanCreationNotAllowedException(beanName,
						"Singleton bean creation not allowed while singletons of this factory are in destruction " +
						"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
			}
			// 在单例创建之前回调。
			beforeSingletonCreation(beanName);
			boolean newSingleton = false;
			boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
			if (recordSuppressedExceptions) {
				this.suppressedExceptions = new LinkedHashSet<>();
			}
			try {
				// 创建bean实例
				singletonObject = singletonFactory.getObject();
				newSingleton = true;
			}
			catch (IllegalStateException ex) {
				// Has the singleton object implicitly appeared in the meantime ->
				// if yes, proceed with it since the exception indicates that state.
				// 在此期间是否隐式地出现了单例对象——>如果是,则继续处理它,因为异常指示了该状态。
				singletonObject = this.singletonObjects.get(beanName);
				if (singletonObject == null) {
					throw ex;
				}
			}
			catch (BeanCreationException ex) {
				if (recordSuppressedExceptions) {
					for (Exception suppressedException : this.suppressedExceptions) {
						ex.addRelatedCause(suppressedException);
					}
				}
				throw ex;
			}
			finally {
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = null;
				}
				// 在单例创建后回调。
				afterSingletonCreation(beanName);
			}
			if (newSingleton) {
				// 缓存bean实例
				addSingleton(beanName, singletonObject);
			}
		}
		return singletonObject;
	}
}
  • 前置回调 --> beforeSingletonCreation
  • 创建bean实例 --> singletonFactory.getObject(); (代码太多,本小节不分析
  • 异常处理
  • 后置回调 --> afterSingletonCreation
  • 缓存实例 --> addSingleton
2.3.1.1 beforeSingletonCreation 前置处理
protected void beforeSingletonCreation(String beanName) {
	if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
		throw new BeanCurrentlyInCreationException(beanName);
	}
}
  • 重要变量:
    • 当前在创建检查中排除的bean的名称:inCreationCheckExclusions = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
    • 标记bean是否正在创建中:singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
  • 逻辑:检查循环依赖,如果当前bean已经被标记为正在创建中,则抛出BeanCurrentlyInCreationException
2.3.1.2 afterSingletonCreation 后置处理
protected void afterSingletonCreation(String beanName) {
	if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
		throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
	}
}
  • 逻辑:与 beforeSingletonCreation 相反,因为bean已经创建完成,需要从 singletonsCurrentlyInCreation 移除
2.3.1.3 addSingleton 缓存bean实例
protected void addSingleton(String beanName, Object singletonObject) {
	// 加锁
	synchronized (this.singletonObjects) {
		// 单例对象的缓存:从bean名称到bean实例。
		this.singletonObjects.put(beanName, singletonObject);
		// 单例工厂的缓存:从bean名到ObjectFactory。
		this.singletonFactories.remove(beanName);
		// 早期单例对象的缓存:从bean名称到bean实例。
		this.earlySingletonObjects.remove(beanName);
		// 一组已注册的单例,包含按注册顺序排列的bean名称。
		this.registeredSingletons.add(beanName);
	}
}
  • 变量定义:
    • private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    • private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    • private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
    • private final Set registeredSingletons = new LinkedHashSet<>(256);
  • 主要逻辑
    • 将创建的Bean实例加入到 ConcurrentHashMap 中
    • 如果刚开始看Spring源码,只需要理解 singletonObjects 即可。
2.3.2 createBean
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;
	// 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.
	// 确保此时实际解析了bean类,并在无法存储在共享合并bean定义中的动态解析类的情况下克隆bean定义。
	Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
	if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
		mbdToUse = new RootBeanDefinition(mbd);
		mbdToUse.setBeanClass(resolvedClass);
	}
	// Prepare method overrides.
	// 准备方法覆盖。
	try {
		mbdToUse.prepareMethodOverrides();
	}
	catch (BeanDefinitionValidationException ex) {
		throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
				beanName, "Validation of method overrides failed", ex);
	}
	try {
		// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
		// 让BeanPostProcessors有机会返回一个代理而不是目标bean实例。
		Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
		if (bean != null) {
			return bean;
		}
	}
	catch (Throwable ex) {
		throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
				"BeanPostProcessor before instantiation of bean failed", ex);
	}
	try {
		// 创建bean实例
		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
		if (logger.isTraceEnabled()) {
			logger.trace("Finished creating instance of bean '" + beanName + "'");
		}
		return beanInstance;
	}
	catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
		// A previously detected exception with proper bean creation context already,
		// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
		throw ex;
	}
	catch (Throwable ex) {
		throw new BeanCreationException(
				mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
	}
}
  • 主要方法与变量:
    • RootBeanDefinition Bean定义
    • resolveBeanClass 解析BeanClass
    • prepareMethodOverrides 准备方法覆盖。
    • resolveBeforeInstantiation 让BeanPostProcessors有机会返回一个代理而不是目标bean实例。
    • doCreateBean 真正开始创建bean实例
    • 异常处理
  • 逻辑:该方法依然是做创建Bean的准备工作
  • resolveBeforeInstantiation涉及到AOP相关,放到后续小节分析,下面我们简单介绍下RootBeanDefinition、和prepareMethodOverrides
2.3.1 RootBeanDefinition 简介

关于 RootBeanDefinition 前文已经出现过,为了快速入门,并没有对其做过多介绍,这里我们简单了解一下:

  • 将Bean的定义信息转化为Spring的内部表示。
  • 间接实现了 BeanDefinition 接口,BeanDefinition 描述bean实例,该实例具有属性值、构造函数参数值和具体实现提供的进一步信息。
2.3.2 prepareMethodOverrides

**注意:这里的 method overrides 指方法注入。**该部分对应的源码比较简单,掌握其基本使用,应该都能读懂。下面演示一下lookup-method、replace-method的使用:

  • 新建 BaseTests,为了方便测试,需要一个测试基类。
  • lookup-method 依次新建
    • org.springframework.demo.method.lookup.Car
    • org.springframework.demo.method.lookup.Taxi
    • org.springframework.demo.method.lookup.LookUpTests
  • replace-method 依次新建
    • org.springframework.demo.method.replace.OriginalDog
    • org.springframework.demo.method.replace.ReplaceDog
    • org.springframework.demo.method.replace.ReplaceTests
  • 代码
-- for BaseTests
package org.springframework.demo;

import org.junit.jupiter.api.BeforeAll;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

public class BaseTests {

	public static XmlBeanFactory xmlBeanFactory;

	@BeforeAll
	public static void beforeTest() {
		xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("org/springframework/demo/demo.xml"));
	}
}
-- for lookup-method
package org.springframework.demo.method.lookup;

public abstract class Car {

	// 用于lookup-method注入
	public abstract Taxi createTaxi();

	private Taxi taxi;

	public Taxi getTaxi() {
		return taxi;
	}

	//setter注入
	public void setTaxi(Taxi taxi) {
		this.taxi = taxi;
	}
}

package org.springframework.demo.method.lookup;

public class Taxi {
	public void say() {
		System.out.println("I am a Taxi...");
	}
}

package org.springframework.demo.method.lookup;

import org.junit.jupiter.api.Test;
import org.springframework.demo.BaseTests;

public class LookUpTests extends BaseTests {

	@Test
	public void test(){
		// 测试lookup-method注入
		Car car1 = xmlBeanFactory.getBean("car", Car.class);
		Car car2 = xmlBeanFactory.getBean("car", Car.class);

		System.out.println("Car:singleton,所以animal1==animal2应该为" + (car1 == car2));

		Taxi dog1 = car1.getTaxi();
		Taxi dog2 = car2.getTaxi();
		System.out.println("Taxi:prototype,Car:singleton,未使用lookup-method注入所以dog1==dog2应该为" + (dog1 == dog2));

		//注意:这里是通过createTaxi()方法获取
		Taxi taxi3 = car1.createTaxi();
		Taxi taxi4 = car2.createTaxi();
		System.out.println("Taxi:prototype,Car:singleton,使用了lookup-method注入所以dog3==dog4应该为" + (taxi3 == taxi4));

	}
}

<!-- ====================lookup-method属性注入==================== -->
<bean id="car" class="org.springframework.demo.method.lookup.Car">
	<!--注意:下面这句配置和lookup-method注入没有关系,我们只是为了出于演示和说明配置该bean-->
	<property name="taxi" ref="taxi"/>
	<!--lookup-method注入-->
	<lookup-method name="createTaxi" bean="taxi"/>
</bean>
<bean id="taxi" class="org.springframework.demo.method.lookup.Taxi" scope="prototype"/>
-- for replace-method
package org.springframework.demo.method.replace;

public class OriginalDog {
	public void sayHello() {
		System.out.println("Hello,I am a black dog...");
	}

	public void sayHello(String name) {
		System.out.println("Hello,I am a black dog, my name is " + name);
	}
}

package org.springframework.demo.method.replace;

import org.springframework.beans.factory.support.MethodReplacer;

import java.lang.reflect.Method;
import java.util.Arrays;

public class ReplaceDog implements MethodReplacer {
	@Override
	public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
		System.out.println("Hello, I am a white dog...");

		Arrays.stream(args).forEach(str -> System.out.println("参数:" + str));
		return obj;
	}
}

package org.springframework.demo.method.replace;

import org.junit.jupiter.api.Test;
import org.springframework.demo.BaseTests;

public class ReplaceTests extends BaseTests {

	@Test
	public void test() {
		// 测试replace-method注入
		OriginalDog originalDog = xmlBeanFactory.getBean("originalDogReplaceMethod", OriginalDog.class);
		originalDog.sayHello("输出结果已经被替换了。。。");
	}
}


<!-- ====================replace-method属性注入==================== -->
<bean id="dogReplaceMethod" class="org.springframework.demo.method.replace.ReplaceDog"/>
<bean id="originalDogReplaceMethod" class="org.springframework.demo.method.replace.OriginalDog">
	<replaced-method name="sayHello" replacer="dogReplaceMethod">
		<arg-type match="java.lang.String"></arg-type>
	</replaced-method>
</bean>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲来也无事

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值