本文主要基于 Spring 5.0.6.RELEASE
摘要: 原创出处 http://cmsblogs.com/?p=todo 「小明哥」,谢谢!
作为「小明哥」的忠实读者,「老艿艿」略作修改,记录在理解过程中,参考的资料。
如果从单例缓存中没有获取到单例 Bean 对象,则说明两种两种情况:
- 该 Bean 的 scope 不是 singleton
- 该 Bean 的 scope 是 singleton ,但是没有初始化完成。
针对这两种情况,Spring 是如何处理的呢?统一加载并完成初始化!这部分内容的篇幅较长,拆分为两部分:
- 第一部分,主要是一些检测、parentBeanFactory 以及依赖处理。
- 第二部分则是各个 scope 的初始化。
代码如下:
// AbstractBeanFactory.java |
这段代码主要处理如下几个部分:
<3>
处,检测。若当前 Bean 在创建,则抛出 BeanCurrentlyInCreationException 异常。- 详细解析,见 「1. 检测」 。
<4>
处,如果 beanDefinitionMap 中不存在 beanName 的 BeanDefinition(即在 Spring bean 初始化过程中没有加载),则尝试从 parentBeanFactory 中加载。- 详细解析,见 「2. 检查父类 BeanFactory」 。
<5>
处,判断是否为类型检查。- 详细解析,见 「3. 类型检查」 。
<6>
处,从mergedBeanDefinitions
中获取beanName
对应的 RootBeanDefinition 对象。如果这个 BeanDefinition 是子 Bean 的话,则会合并父类的相关属性。- 详细解析,见 「4. 获取 RootBeanDefinition」 。
<7>
处,依赖处理。- 详细解析,见 「5. 处理依赖」 。
1. 检测
在前面就提过,Spring 只解决单例模式下的循环依赖,对于原型模式的循环依赖则是抛出 BeanCurrentlyInCreationException 异常,所以首先检查该 beanName
是否处于原型模式下的循环依赖。如下:
// AbstractBeanFactory.java |
调用
#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
的#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 的加载》 中,已经有详细解析。<2>
处,如果name
是以“&”
开头的,则加上“&”
,因为在#transformedBeanName(String name)
方法,将“&”
去掉了,这里补上。
3. 类型检查
方法参数 typeCheckOnly
,是用来判断调用 #getBean(...)
方法时,表示是否为仅仅进行类型检查获取 Bean 对象。如果不是仅仅做类型检查,而是创建 Bean 对象,则需要调用 #markBeanAsCreated(String beanName)
方法,进行记录。代码如下:
// AbstractBeanFactory.java |
4. 获取 RootBeanDefinition
// AbstractBeanFactory.java |
调用
#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 |
- 这段代码逻辑是:通过迭代的方式依次对依赖 bean 进行检测、校验。如果通过,则调用
#getBean(String beanName)
方法,实例化依赖的 Bean 对象。
5.1 isDependent
<1>
处,调用 #isDependent(String beanName, String dependentBeanName)
方法,是校验该依赖是否已经注册给当前 Bean 。代码如下:
// DefaultSingletonBeanRegistry.java |
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 |
- 其实将就是该映射关系保存到两个集合中:
dependentBeanMap
、dependenciesForBeanMap
。
5.3 getBean
<3>
处,最后调用 #getBean(String beanName)
方法,实例化依赖 Bean 对象。
6. 小结
至此,加载 bean 的第二个部分也分析完毕了,下篇开始分析第三个部分:各大作用域 bean 的处理。