文章目录
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的名称:
- 逻辑:检查循环依赖,如果当前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>