如果从单列缓存中没有获取到 Bean 对象,则说明有两种情况:
- 该 Bean 的 Scope 不是 Singleton
- 该 Bean 的 Scope 是 Singleton,但是初始化没有完成
针对这两种情况,Spring 是如何处理的呢?统一加载并完成初始化!这部分内容的篇幅较长,拆分为两部分:
- 第一部分,主要是一些检测、parentBeanFactory 以及依赖处理。
- 第二部分则是各个 scope 的初始化。
代码如下:
//AbstractBeanFactory.java
// <3> 因为 Spring 只解决单例模式下得循环依赖,在原型模式下如果存在循环依赖则会抛出异常。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// <4> 如果当前容器中没有找到,则从父类容器中加载
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
String nameToLookup = originalBeanName(name);
// 如果,父类容器为 AbstractBeanFactory ,直接递归查找
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
// 用明确的 args 从 parentBeanFactory 中,获取 Bean 对象
} else if (args != null) {
return (T)parentBeanFactory.getBean(nameToLookup, args);
// 用明确的 requiredType 从 parentBeanFactory 中,获取 Bean 对象
} else if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
// 直接使用 nameToLookup 从 parentBeanFactory 获取 Bean 对象
} 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);
// <7> 处理所依赖的 bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
// 若给定的依赖 bean 已经注册为依赖给定的 bean
// 即循环依赖的情况,抛出 BeanCreationException 异常
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 缓存依赖调用 TODO 芋艿
registerDependentBean(dep, beanName);
try {
// 递归处理依赖 Bean
getBean(dep);
} catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// ... 省略很多代码
}复制代码
这段代码主要处理以下几个部分:
- <3> 处,原型模式循环依赖检查,如果当前 Bean 在创建,则抛出异常。详细解析见「1. 循环依赖检查」
- <4> 处,如果 beanDefinitionMap 中不存在 beanName 的 BeanDefinition(即在 Spring bean 初始化过程中没有加载),则尝试从 parentBeanFactory 中加载。详细解析见「2. 检查父类 BeanFactory」
- <5> 处,判断是否为类型检查。详细解析见「3. 类型检查」
- <6> 处,从 mergedBeanDefinitions 中获取 beanName 对应的 RootBeanDefinition 对象。如果这个 BeanDefinition 是子 Bean 的话,则会合并父类的相关属性。详细解析见「4. 获取 RootBeanDefinition」
- <7> 处
1. 循环依赖检查
Spring 只解决单列模式下的循环依赖,对于原型模式的依赖则抛出 BeanCurrentlyInCreationException 异常
//AbstractBeanFactory.java
// 因为 Spring 只解决单例模式下得循环依赖,在原型模式下如果存在循环依赖则会抛出异常。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
复制代码
调用 AbstractBeanFactory#isPrototypeCurrentlyInCreation(String beanName) 方法,判断当前 Bean 是否正在创建。代码如下:
//AbstractBeanFactory.java
//ThreaadLocal 缓存创建的Bean
private final ThreadLocal<Object> prototypesCurrentlyInCreation = new NamedThreadLocal<>("Prototype beans currently in creation");
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName)|| (curVal instanceof Set && ((Set<?>)curVal).contains(beanName))));
}复制代码
ThreadLocal 存储的值在单列模式和原型模式是不一样的
- 单列模式存储的是 Set 集合
- 原型模式存储的是 String
2. 检查父类 BeanFactory
如果当前容器没有找到 Bean ,就从父容器中加载。代码如下:
//AbstractBeanFactory.java
// 获取父容器
BeanFactory parentBeanFactory = getParentBeanFactory();
// 检查父容器
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 获取原始 beanName
String nameToLookup = originalBeanName(name);
// 如果,父类容器为 AbstractBeanFactory ,直接递归查找
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
// 用明确的 args 从 parentBeanFactory 中,获取 Bean 对象
} else if (args != null) {
return (T)parentBeanFactory.getBean(nameToLookup, args);
// 用明确的 requiredType 从 parentBeanFactory 中,获取 Bean 对象
} else if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
// 直接使用 nameToLookup 从 parentBeanFactory 获取 Bean 对象
} else {
return (T)parentBeanFactory.getBean(nameToLookup);
}
}复制代码
2.1 获取父容器
获取父容器。代码如下:
//AbstractBeanFactory.java
private BeanFactory parentBeanFactory;
@Override
public BeanFactory getParentBeanFactory() {
return this.parentBeanFactory;
}复制代码
2.2 检查父容器
若父容器不为空,并且 beanDefinitionMap 中没有找到对应的 BeanDefinition 对象 。代码如下:
//DefaultListableBeanFactory.java
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
@Override
public boolean containsBeanDefinition(String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
return this.beanDefinitionMap.containsKey(beanName);
}复制代码
2.3 获取原始 beanName
获取原始 beanName。代码如下:
//AbstractBeanFactory.java
protected String originalBeanName(String name) {
String beanName = transformedBeanName(name); // <x>
if (name.startsWith(FACTORY_BEAN_PREFIX)) { // <y>
beanName = FACTORY_BEAN_PREFIX + beanName;
}
return beanName;
}复制代码
- <x> 处,AbstractBeanFactory#transformedBeanName(String name) 方法,是对 name 进行转换,获取真正的 beanName。详情解析前往《【Spring 源码】—— IoC 之开启 Bean 的加载》
- <y> 处,如果 name 是以 "&" 开头的,则加上 "&" ,因为在AbstractBeanFactory#transformedBeanName(String name) 方法中把 "&" 去掉,这边需要补上
2.4 委托父容器加载
这里都是委托父容器加载 Bean 的逻辑。代码如下:
// 如果,父类容器为 AbstractBeanFactory ,直接递归查找
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
// 用明确的 args 从 parentBeanFactory 中,获取 Bean 对象
} else if (args != null) {
return (T)parentBeanFactory.getBean(nameToLookup, args);
// 用明确的 requiredType 从 parentBeanFactory 中,获取 Bean 对象
} else if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
// 直接使用 nameToLookup 从 parentBeanFactory 获取 Bean 对象
} else {
return (T)parentBeanFactory.getBean(nameToLookup);
}复制代码
3.类型检查
方法参数 typeCheckOnly,表示仅仅进行类型检查获取 Bean 对象。如果不是仅仅做类型检查,而是创建 Bean 对象,则需要调用 AbstractBeanFactory#markBeanAsCreated(String beanName) 方法,进行记录。代码如下:
//AbstractBeanFactory.java
// beanName -> RootBeanDefinition
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
protected void markBeanAsCreated(String beanName) {
// 没有创建
if (!this.alreadyCreated.contains(beanName)) {
// 加上全局锁
synchronized (this.mergedBeanDefinitions) {
// 再次检查一次:DCL 双检查模式
if (!this.alreadyCreated.contains(beanName)) {
// 从 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);复制代码
调用 AbstractBeanFactory#getMergedLocalBeanDefinition(String beanName) 获取 RootBeanDefinition 对象。代码如下:
//AbstractBeanFactory.java
// beanName -> RootBeanDefinition
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// 快速从缓存中获取,如果不为空,则直接返回
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd != null) {
return mbd;
}
// 如果返回的 BeanDefinition 是子类 bean 的话,则合并父类相关属性
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
复制代码
- 如果 mergedBeanDefinitions 缓存中存在对应的 RootBeanDefinition 对象,则直接返回。
- 否则调用AbstractBeanFactory#getMergedBeanDefinition(String beanName, BeanDefinition bd)获取 RootBeanDefinition 对象
- 通过 AbstractBeanFactory#getBeanDefinition(String beanName) 获取 BeanDefinition 对象,如果没有找到就抛出 NoSuchBeanDefinitionException 异常。代码如下:
// DefaultListableBeanFactory.java
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// AbstractBeanFactory.java
@Override
public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
BeanDefinition bd = this.beanDefinitionMap.get(beanName);
if (bd == null) {
if (logger.isTraceEnabled()) {
logger.trace("No bean named '" + beanName + "' found in " + this);
}
throw new NoSuchBeanDefinitionException(beanName);
}
return bd;
}复制代码
5.