本文主要基于 Spring 5.0.6.RELEASE
摘要: 原创出处 http://cmsblogs.com/?p=todo 「小明哥」,谢谢!
作为「小明哥」的忠实读者,「老艿艿」略作修改,记录在理解过程中,参考的资料。
在 Spring 中存在着不同的 scope,默认是 singleton ,还有 prototype、request 等等其他的 scope 。他们的初始化步骤是怎样的呢?这个答案在这篇博客中给出。
1. singleton
Spring 的 scope 默认为 singleton ,其初始化的代码如下:
// AbstractBeanFactory.java |
在 《【死磕 Spring】—— IoC 之加载 Bean:从单例缓存中获取单》 中,已经分析了从缓存中获取单例模式的 bean 。但是如果缓存中不存在呢?则需要从头开始加载 Bean ,这个过程由
#getSingleton(String beanName, ObjectFactory<?> singletonFactory)
方法来实现。代码如下:// DefaultSingletonBeanRegistry.java
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 全局加锁
synchronized (this.singletonObjects) {
// <1> 从缓存中检查一遍
// 因为 singleton 模式其实就是复用已经创建的 bean 所以这步骤必须检查
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 + "'");
}
// <2> 加载前置处理
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// <3> 初始化 bean
// 这个过程其实是调用 createBean() 方法
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;
}
// <4> 后置处理
afterSingletonCreation(beanName);
}
// <5> 加入缓存中
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}- 其实,这个过程并没有真正创建 Bean 对象,仅仅只是做了一部分准备和预处理步骤。真正获取单例 bean 的方法,其实是由
<3>
处的singletonFactory.getObject()
这部分代码块来实现,而singletonFactory
由回调方法产生。 - 那么这个方法做了哪些准备呢?
<1>
处,再次检查缓存是否已经加载过,如果已经加载了则直接返回,否则开始加载过程。<2>
处,调用#beforeSingletonCreation(String beanName)
方法,记录加载单例 bean 之前的加载状态,即前置处理。在 《【死磕 Spring】—— IoC 之加载 Bean:从单例缓存中获取单例 Bean》 中,已经详细解析。<3>
处,调用参数传递的 ObjectFactory 的#getObject()
方法,实例化 bean 。【重要】后续文章,详细解析。<4>
处,调用#afterSingletonCreation(String beanName)
方法,进行加载单例后的后置处理。在 《【死磕 Spring】—— IoC 之加载 Bean:从单例缓存中获取单例 Bean》 中,已经详细解析。<5>
处,调用#addSingleton(String beanName, Object singletonObject)
方法,将结果记录并加入值缓存中,同时删除加载 bean 过程中所记录的一些辅助状态。详细解析,见 「1.1 addSingleton」 。
- 其实,这个过程并没有真正创建 Bean 对象,仅仅只是做了一部分准备和预处理步骤。真正获取单例 bean 的方法,其实是由
- 在
<x>
处,加载了单例 bean 后,调用#getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd)
方法,从 bean 实例中获取对象。该方法已经在 《【死磕 Spring】—— IoC 之加载 Bean:从单例缓存中获取单例 Bean》 中,详细分析了。
1.1 addSingleton
// DefaultSingletonBeanRegistry.java |
- 一个 put、一个 add、两个 remove 操作。
- 【put】
singletonObjects
属性,单例 bean 的缓存。 - 【remove】
singletonFactories
属性,单例 bean Factory 的缓存。 - 【remove】
earlySingletonObjects
属性,“早期”创建的单例 bean 的缓存。 - 【add】
registeredSingletons
属性,已经注册的单例缓存。
2. 原型模式
// AbstractBeanFactory.java |
原型模式的初始化过程很简单:直接创建一个新的 Bean 的实例就可以了。过程如下:
- 在
<1>
处,调用#beforePrototypeCreation(String beanName)
方法,记录加载原型模式 bean 之前的加载状态,即前置处理。详细解析,见 「2.1 beforePrototypeCreation」 。 - 在
<2>
处,调用#createBean(String beanName)
方法,创建一个 bean 实例对象。【重要】后续文章,详细解析。 - 在
<3>
处,调用#afterSingletonCreation(String beanName)
方法,进行加载原型模式 bean 后的后置处理。详细解析,见 「2.3 afterSingletonCreation」 。 - 在
<4>
处,加载了单例 bean 后,调用#getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd)
方法,从 bean 实例中获取对象。该方法已经在 《【死磕 Spring】—— IoC 之加载 Bean:从单例缓存中获取单例 Bean》 中,详细分析了。
2.1 beforePrototypeCreation
// AbstractBeanFactory.java |
2.2 afterSingletonCreation
// AbstractBeanFactory.java |
3. 其它作用域
// AbstractBeanFactory.java |
核心流程和原型模式一样,只不过获取 bean 实例是由
Scope#get(String name, ObjectFactory<?> objectFactory)
方法来实现。代码如下:// SimpleThreadScope.java
private final ThreadLocal<Map<String, Object>> threadScope =
new NamedThreadLocal<Map<String, Object>>("SimpleThreadScope") {
protected Map<String, Object> initialValue() {
return new HashMap<>();
}
};
public Object get(String name, ObjectFactory<?> objectFactory) {
// 获取 scope 缓存
Map<String, Object> scope = this.threadScope.get();
Object scopedObject = scope.get(name);
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
// 加入缓存
scope.put(name, scopedObject);
}
return scopedObject;
}org.springframework.beans.factory.config.Scope
接口,有多种实现类。其他的 Scope 实现类,感兴趣的胖友,可以单独去看。
4. 小结
对于上面三个模块,其中最重要的有两个方法:
- 一个是
#createBean(String beanName, RootBeanDefinition mbd, Object[] args)
方法。 - 一个是
#getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd)
方法。
这两个方法在上面三个模块都有调用。
#createBean(String beanName, RootBeanDefinition mbd, Object[] args)
方法,后续详细说明。#getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd)
方法,在博客 《【死磕 Spring】—— IoC 之加载 Bean:从单例缓存中获取单》 中有详细讲解。这里再次阐述下(此段内容来自《Spring 源码深度解析》):这个方法主要是验证以下我们得到的 bean 的正确性,其实就是检测当前 bean 是否是 FactoryBean 类型的 bean 。
如果是,那么需要调用该 bean 对应的 FactoryBean 实例的
#getObject()
方法,作为返回值。无论是从缓存中获得到的 bean 还是通过不同的 scope 策略加载的 bean 都只是最原始的 bean 状态,并不一定就是我们最终想要的 bean。
举个例子,加入我们需要对工厂 bean 进行处理,那么这里得到的其实是工厂 bean 的初始状态,但是我们真正需要的是工厂 bean 中定义
factory-method
方法中返回的 bean,而#getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd)
方法,就是完成这个工作的。
至此,Spring 加载 bean 的三个部分(LZ自己划分的)已经分析完毕了。