code-learning/spring/23-Spring-IoC 之加载 Bean:分析各 scope 的 Bean 创建.md

330 lines
15 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 【死磕 Spring】—— IoC 之加载 Bean分析各 scope 的 Bean 创建
**本文主要基于 Spring 5.0.6.RELEASE**
摘要: 原创出处 http://cmsblogs.com/?p=todo 「小明哥」,谢谢!
作为「小明哥」的忠实读者,「老艿艿」略作修改,记录在理解过程中,参考的资料。
------
在 Spring 中存在着不同的 scope默认是 singleton ,还有 prototype、request 等等其他的 scope 。他们的初始化步骤是怎样的呢?这个答案在这篇博客中给出。
# 1. singleton
Spring 的 scope 默认为 singleton ,其初始化的代码如下:
```
// AbstractBeanFactory.java
if (mbd.isSingleton()) { // 单例模式
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
// 显式从单例缓存中删除 Bean 实例
// 因为单例模式下为了解决循环依赖,可能他已经存在了,所以销毁它。 TODO 芋艿
destroySingleton(beanName);
throw ex;
}
});
// <x>
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
```
- 在 [《【死磕 Spring】—— IoC 之加载 Bean从单例缓存中获取单》](http://svip.iocoder.cn/Spring/IoC-get-Bean-getSingleton) 中,已经分析了从缓存中获取单例模式的 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》](http://svip.iocoder.cn/Spring/IoC-get-Bean-getSingleton) 中,已经详细解析。
- `<3>` 处,调用参数传递的 ObjectFactory 的 `#getObject()` 方法,实例化 bean 。【重要】后续文章,详细解析。
- `<4>` 处,调用 `#afterSingletonCreation(String beanName)` 方法,进行加载单例后的后置处理。在 [《【死磕 Spring】—— IoC 之加载 Bean从单例缓存中获取单例 Bean》](http://svip.iocoder.cn/Spring/IoC-get-Bean-getSingleton) 中,已经详细解析。
- `<5>` 处,调用 `#addSingleton(String beanName, Object singletonObject)` 方法,将结果记录并加入值缓存中,同时删除加载 bean 过程中所记录的一些辅助状态。详细解析,见 [「1.1 addSingleton」](https://svip.iocoder.cn/Spring/IoC-get-Bean-different-scope/#) 。
- 在 `<x>` 处,加载了单例 bean 后,调用 `#getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd)` 方法,从 bean 实例中获取对象。该方法已经在 [《【死磕 Spring】—— IoC 之加载 Bean从单例缓存中获取单例 Bean》](http://svip.iocoder.cn/Spring/IoC-get-Bean-getSingleton) 中,详细分析了。
## 1.1 addSingleton
```
// DefaultSingletonBeanRegistry.java
/**
* Cache of singleton objects: bean name to bean instance.
*
* 存放的是单例 bean 的映射。
*
* 对应关系为 bean name --> bean instance
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* Cache of singleton factories: bean name to ObjectFactory.
*
* 存放的是【早期】的单例 bean 的映射。
*
* 对应关系也是 bean name --> bean instance。
*
* 它与 {@link #singletonObjects} 的区别区别在,于 earlySingletonObjects 中存放的 bean 不一定是完整的。
*
* 从 {@link #getSingleton(String)} 方法中中我们可以了解bean 在创建过程中就已经加入到 earlySingletonObjects 中了,
* 所以当在 bean 的创建过程中就可以通过 getBean() 方法获取。
* 这个 Map 也是解决【循环依赖】的关键所在。
**/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/**
* Cache of early singleton objects: bean name to bean instance.
*
* 存放的是 ObjectFactory 的映射,可以理解为创建单例 bean 的 factory 。
*
* 对应关系是 bean name --> ObjectFactory
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Set of registered singletons, containing the bean names in registration order. */
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
```
- 一个 put、一个 add、两个 remove 操作。
- 【put】`singletonObjects` 属性,单例 bean 的缓存。
- 【remove】`singletonFactories` 属性,单例 bean Factory 的缓存。
- 【remove】`earlySingletonObjects` 属性,“早期”创建的单例 bean 的缓存。
- 【add】`registeredSingletons` 属性,已经注册的单例缓存。
# 2. 原型模式
```
// AbstractBeanFactory.java
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
// <1> 加载前置处理
beforePrototypeCreation(beanName);
// <2> 创建 Bean 对象
prototypeInstance = createBean(beanName, mbd, args);
} finally {
// <3> 加载后缀处理
afterPrototypeCreation(beanName);
}
// <4> 从 Bean 实例中获取对象
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
```
原型模式的初始化过程很简单:直接创建一个**新**的 Bean 的实例就可以了。过程如下:
- 在 `<1>` 处,调用 `#beforePrototypeCreation(String beanName)` 方法,记录加载原型模式 bean 之前的加载状态,即前置处理。详细解析,见 [「2.1 beforePrototypeCreation」](https://svip.iocoder.cn/Spring/IoC-get-Bean-different-scope/#) 。
- 在 `<2>` 处,调用 `#createBean(String beanName)` 方法,创建一个 bean 实例对象。【重要】后续文章,详细解析。
- 在 `<3>` 处,调用 `#afterSingletonCreation(String beanName)` 方法,进行加载原型模式 bean 后的后置处理。详细解析,见 [「2.3 afterSingletonCreation」](https://svip.iocoder.cn/Spring/IoC-get-Bean-different-scope/#) 。
- 在 `<4>` 处,加载了单例 bean 后,调用 `#getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd)` 方法,从 bean 实例中获取对象。该方法已经在 [《【死磕 Spring】—— IoC 之加载 Bean从单例缓存中获取单例 Bean》](http://svip.iocoder.cn/Spring/IoC-get-Bean-getSingleton) 中,详细分析了。
## 2.1 beforePrototypeCreation
```
// AbstractBeanFactory.java
/** Names of beans that are currently in creation. */
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
new NamedThreadLocal<>("Prototype beans currently in creation");
protected void beforePrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal == null) { // String
this.prototypesCurrentlyInCreation.set(beanName);
} else if (curVal instanceof String) { // String => Set
Set<String> beanNameSet = new HashSet<>(2);
beanNameSet.add((String) curVal);
beanNameSet.add(beanName);
this.prototypesCurrentlyInCreation.set(beanNameSet);
} else { // Set
Set<String> beanNameSet = (Set<String>) curVal;
beanNameSet.add(beanName);
}
}
```
## 2.2 afterSingletonCreation
```
// AbstractBeanFactory.java
protected void afterPrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal instanceof String) { // String => null
this.prototypesCurrentlyInCreation.remove();
} else if (curVal instanceof Set) { // Set
Set<String> beanNameSet = (Set<String>) curVal;
beanNameSet.remove(beanName);
if (beanNameSet.isEmpty()) { // Set => null
this.prototypesCurrentlyInCreation.remove();
}
}
}
```
# 3. 其它作用域
```
// AbstractBeanFactory.java
else {
// 获得 scopeName 对应的 Scope 对象
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
// 从指定的 scope 下创建 bean
Object scopedInstance = scope.get(beanName, () -> {
// 加载前置处理
beforePrototypeCreation(beanName);
try {
// 创建 Bean 对象
return createBean(beanName, mbd, args);
} finally {
// 加载后缀处理
afterPrototypeCreation(beanName);
}
});
// 从 Bean 实例中获取对象
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
} catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
```
- **核心流程和原型模式一样**,只不过获取 bean 实例是由 `Scope#get(String name, ObjectFactory<?> objectFactory)` 方法来实现。代码如下:
```
// SimpleThreadScope.java
private final ThreadLocal<Map<String, Object>> threadScope =
new NamedThreadLocal<Map<String, Object>>("SimpleThreadScope") {
@Override
protected Map<String, Object> initialValue() {
return new HashMap<>();
}
};
@Override
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从单例缓存中获取单》](http://svip.iooder.cn/Spring/IoC-get-Bean-getSingleton) 中有详细讲解。这里再次阐述下(此段内容来自《**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自己划分的已经分析完毕了。