code-learning/spring/22-Spring-IoC 之加载 Bean:parentBeanFactory 与依赖处理.md

475 lines
18 KiB
Markdown
Raw Permalink 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 之加载 BeanparentBeanFactory 与依赖处理
**本文主要基于 Spring 5.0.6.RELEASE**
摘要: 原创出处 http://cmsblogs.com/?p=todo 「小明哥」,谢谢!
作为「小明哥」的忠实读者,「老艿艿」略作修改,记录在理解过程中,参考的资料。
------
如果从单例缓存中没有获取到单例 Bean 对象,则说明两种两种情况:
1. 该 Bean 的 scope 不是 singleton
2. 该 Bean 的 scope 是 singleton ,但是没有初始化完成。
针对这两种情况Spring 是如何处理的呢?统一加载并完成初始化!这部分内容的篇幅较长,拆分为两部分:
- 第一部分主要是一些检测、parentBeanFactory 以及依赖处理。
- 第二部分则是各个 scope 的初始化。
代码如下:
```
// AbstractBeanFactory.java
//protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
// ... 省略很多代码
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
// <3> 因为 Spring 只解决单例模式下得循环依赖,在原型模式下如果存在循环依赖则会抛出异常。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// <4> 如果容器中没有找到,则从父类容器中加载
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
} else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
} else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
} else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
// <5> 如果不是仅仅做类型检查则是创建bean这里需要记录
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
// <6> 从容器中获取 beanName 相应的 GenericBeanDefinition 对象,并将其转换为 RootBeanDefinition 对象
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 检查给定的合并的 BeanDefinition
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// <7> 处理所依赖的 bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
// 若给定的依赖 bean 已经注册为依赖给定的 bean
// 循环依赖的情况
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 缓存依赖调用
registerDependentBean(dep, beanName);
try {
getBean(dep);
} catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
}
// ... 省略很多代码
```
这段代码主要处理如下几个部分:
- ```
<3>
```
处,检测。若当前 Bean 在创建,则抛出 BeanCurrentlyInCreationException 异常。
- 详细解析,见 [「1. 检测」](https://svip.iocoder.cn/Spring/IoC-get-Bean-parentBeanFactory-and-depend-on/#) 。
- ```
<4>
```
处,如果 beanDefinitionMap 中不存在 beanName 的 BeanDefinition即在 Spring bean 初始化过程中没有加载),则尝试从 parentBeanFactory 中加载。
- 详细解析,见 [「2. 检查父类 BeanFactory」](https://svip.iocoder.cn/Spring/IoC-get-Bean-parentBeanFactory-and-depend-on/#) 。
- ```
<5>
```
处,判断是否为类型检查。
- 详细解析,见 [「3. 类型检查」](https://svip.iocoder.cn/Spring/IoC-get-Bean-parentBeanFactory-and-depend-on/#) 。
- ```
<6>
```
处,从
```
mergedBeanDefinitions
```
中获取
```
beanName
```
对应的 RootBeanDefinition 对象。如果这个 BeanDefinition 是子 Bean 的话,则会合并父类的相关属性。
- 详细解析,见 [「4. 获取 RootBeanDefinition」](https://svip.iocoder.cn/Spring/IoC-get-Bean-parentBeanFactory-and-depend-on/#) 。
- ```
<7>
```
处,依赖处理。
- 详细解析,见 [「5. 处理依赖」](https://svip.iocoder.cn/Spring/IoC-get-Bean-parentBeanFactory-and-depend-on/#) 。
# 1. 检测
在前面就提过Spring 只解决单例模式下的循环依赖,对于原型模式的循环依赖则是抛出 BeanCurrentlyInCreationException 异常,所以首先检查该 `beanName` 是否处于原型模式下的循环依赖。如下:
```
// AbstractBeanFactory.java
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
```
- 调用 `#isPrototypeCurrentlyInCreation(String beanName)` 方法,判断当前 Bean 是否正在创建。代码如下:
```
// AbstractBeanFactory.java
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) // 相等
|| (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); // 包含
}
```
- 其实检测逻辑和单例模式一样,一个“集合”存放着正在创建的 Bean ,从该集合中进行判断即可,只不过单例模式的“集合”为 Set ,而原型模式的则是 ThreadLocal 。`prototypesCurrentlyInCreation` 定义如下:
```
// AbstractBeanFactory.java
/** Names of beans that are currently in creation. */
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
new NamedThreadLocal<>("Prototype beans currently in creation");
```
# 2. 检查父类 BeanFactory
若 `#containsBeanDefinition(String beanName)` 方法中不存在 `beanName` 相对应的 BeanDefinition 对象时,则从 `parentBeanFactory` 中获取。代码如下:
```
// AbstractBeanFactory.java
// 获取 parentBeanFactory
BeanFactory parentBeanFactory = getParentBeanFactory();
// parentBeanFactory 不为空且 beanDefinitionMap 中不存该 name 的 BeanDefinition
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 确定原始 beanName
String nameToLookup = originalBeanName(name);
// 若为 AbstractBeanFactory 类型,委托父类处理
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
} else if (args != null) {
// 委托给构造函数 getBean() 处理
return (T) parentBeanFactory.getBean(nameToLookup, args);
} else {
// 没有 args委托给标准的 getBean() 处理
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
```
- 整个过程较为简单,都是委托 `parentBeanFactory` 的 `#getBean(...)` 方法来进行处理,只不过在获取之前对 `breanName` 进行简单的处理,主要是想获取原始的 `beanName` 。代码如下:
```
// AbstractBeanFactory.java
protected String originalBeanName(String name) {
String beanName = transformedBeanName(name); // <1>
if (name.startsWith(FACTORY_BEAN_PREFIX)) { // <2>
beanName = FACTORY_BEAN_PREFIX + beanName;
}
return beanName;
}
```
- `<1>` 处,`#transformedBeanName(String name)` 方法,是对 `name` 进行转换,获取真正的 beanName 。在 [《【死磕 Spring】—— IoC 之开启 Bean 的加载》](http://svip.iocoder.cn/Spring/IoC-get-Bean-begin) 中,已经有详细解析。
- `<2>` 处,如果 `name` 是以 `&` 开头的,则加上 `&` ,因为在 `#transformedBeanName(String name)` 方法,将 `&` 去掉了,这里**补上**。
# 3. 类型检查
方法参数 `typeCheckOnly` ,是用来判断调用 `#getBean(...)` 方法时,表示是否为**仅仅**进行类型检查获取 Bean 对象。如果不是仅仅做类型检查,而是创建 Bean 对象,则需要调用 `#markBeanAsCreated(String beanName)` 方法,进行记录。代码如下:
```
// AbstractBeanFactory.java
/**
* Names of beans that have already been created at least once.
*
* 已创建 Bean 的名字集合
*/
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
protected void markBeanAsCreated(String beanName) {
// 没有创建
if (!this.alreadyCreated.contains(beanName)) {
// 加上全局锁
synchronized (this.mergedBeanDefinitions) {
// 再次检查一次DCL 双检查模式
if (!this.alreadyCreated.contains(beanName)) {
// Let the bean definition get re-merged now that we're actually creating
// the bean... just in case some of its metadata changed in the meantime.
// 从 mergedBeanDefinitions 中删除 beanName并在下次访问时重新创建它。
clearMergedBeanDefinition(beanName);
// 添加到已创建 bean 集合中
this.alreadyCreated.add(beanName);
}
}
}
}
protected void clearMergedBeanDefinition(String beanName) {
this.mergedBeanDefinitions.remove(beanName);
}
```
# 4. 获取 RootBeanDefinition
```
// AbstractBeanFactory.java
// 从容器中获取 beanName 相应的 GenericBeanDefinition 对象,并将其转换为 RootBeanDefinition 对象
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 检查给定的合并的 BeanDefinition
checkMergedBeanDefinition(mbd, beanName, args);
```
- 调用 `#getMergedLocalBeanDefinition(String beanName)` 方法,获取相对应的 BeanDefinition 对象。代码如下:
```
// AbstractBeanFactory.java
/** Map from bean name to merged RootBeanDefinition. */
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// Quick check on the concurrent map first, with minimal locking.
// 快速从缓存中获取,如果不为空,则直接返回
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd != null) {
return mbd;
}
// 获取 RootBeanDefinition
// 如果返回的 BeanDefinition 是子类 bean 的话,则合并父类相关属性
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
```
- 首先,直接从 `mergedBeanDefinitions` 缓存中获取相应的 RootBeanDefinition 对象,如果存在则直接返回。
- 否则,调用 `#getMergedBeanDefinition(String beanName, BeanDefinition bd)` 方法,获取 RootBeanDefinition 对象。若获取的 BeanDefinition 为**子** BeanDefinition则需要合并**父类**的相关属性。关于该方法的源码,本文不做详细解析。感兴趣的胖友,可以自己研究。
- 调用 `#checkMergedBeanDefinition()` 方法,检查给定的合并的 BeanDefinition 对象。代码如下:
```
// AbstractBeanFactory.java
protected void checkMergedBeanDefinition(RootBeanDefinition mbd, String beanName, @Nullable Object[] args)
throws BeanDefinitionStoreException {
if (mbd.isAbstract()) {
throw new BeanIsAbstractException(beanName);
}
}
```
# 5. 处理依赖
如果一个 Bean 有依赖 Bean 的话,那么在初始化该 Bean 时是需要先初始化它所依赖的 Bean 。代码如下:
```
// AbstractBeanFactory.java
// Guarantee initialization of beans that the current bean depends on.
// 处理所依赖的 bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
// <1> 若给定的依赖 bean 已经注册为依赖给定的 bean
// 即循环依赖的情况,抛出 BeanCreationException 异常
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// <2> 缓存依赖调用 TODO 芋艿
registerDependentBean(dep, beanName);
try {
// <3> 递归处理依赖 Bean
getBean(dep);
} catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
```
- 这段代码逻辑是:通过迭代的方式依次对依赖 bean 进行检测、校验。如果通过,则调用 `#getBean(String beanName)` 方法,实例化**依赖**的 Bean 对象。
## 5.1 isDependent
`<1>` 处,调用 `#isDependent(String beanName, String dependentBeanName)` 方法,是校验该依赖是否已经注册给当前 Bean 。代码如下:
```
// DefaultSingletonBeanRegistry.java
/**
* Map between dependent bean names: bean name to Set of dependent bean names.
*
* 保存的是依赖 beanName 之间的映射关系beanName - > 依赖 beanName 的集合
*/
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
protected boolean isDependent(String beanName, String dependentBeanName) {
synchronized (this.dependentBeanMap) {
return isDependent(beanName, dependentBeanName, null);
}
}
```
- `dependentBeanMap` 对象保存的是依赖 `beanName` 之间的映射关系:`beanName` - > 依赖 `beanName` 的集合。
- 同步加锁给 `dependentBeanMap` 对象,然后调用 `#isDependent(String beanName, String dependentBeanName, Set<String> alreadySeen)` 方法,进行校验。代码如下:
```
// DefaultSingletonBeanRegistry.java
private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
// alreadySeen 已经检测的依赖 bean
if (alreadySeen != null && alreadySeen.contains(beanName)) {
return false;
}
// 获取原始 beanName
String canonicalName = canonicalName(beanName);
// 获取当前 beanName 的依赖集合
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans == null) {
return false;
}
// 存在,则证明存在已经注册的依赖
if (dependentBeans.contains(dependentBeanName)) {
return true;
}
// 递归检测依赖
for (String transitiveDependency : dependentBeans) {
if (alreadySeen == null) {
alreadySeen = new HashSet<>();
}
// 添加到 alreadySeen 中
alreadySeen.add(beanName);
// 递推
if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
return true;
}
}
return false;
}
```
- 代码比较长,当然也有点绕。感兴趣的胖友,可以调试下。
## 5.2 registerDependentBean
`<2>` 处,如果校验成功,则调用 `#registerDependentBean(String beanName, String dependentBeanName)` 方法,将该依赖进行注册,便于在销毁 Bean 之前对其进行销毁。代码如下:
```
// DefaultSingletonBeanRegistry.java
/**
* Map between dependent bean names: bean name to Set of dependent bean names.
*
* 保存的是依赖 beanName 之间的映射关系beanName - > 依赖 beanName 的集合
*/
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
/**
* Map between depending bean names: bean name to Set of bean names for the bean's dependencies.
*
* 保存的是依赖 beanName 之间的映射关系:依赖 beanName - > beanName 的集合
*/
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
public void registerDependentBean(String beanName, String dependentBeanName) {
// 获取 beanName
String canonicalName = canonicalName(beanName);
// 添加 <canonicalName, <dependentBeanName>> 到 dependentBeanMap 中
synchronized (this.dependentBeanMap) {
Set<String> dependentBeans =
this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
if (!dependentBeans.add(dependentBeanName)) {
return;
}
}
// 添加 <dependentBeanName, <canonicalName>> 到 dependenciesForBeanMap 中
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean =
this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
dependenciesForBean.add(canonicalName);
}
}
```
- 其实将就是该映射关系保存到两个集合中:`dependentBeanMap`、`dependenciesForBeanMap` 。
## 5.3 getBean
`<3>` 处,最后调用 `#getBean(String beanName)` 方法,实例化依赖 Bean 对象。
# 6. 小结
至此,加载 bean 的第二个部分也分析完毕了,下篇开始分析第三个部分:各大作用域 bean 的处理。