本文主要基于 Spring 5.0.6.RELEASE
摘要: 原创出处 http://cmsblogs.com/?p=todo 「小明哥」,谢谢!
作为「小明哥」的忠实读者,「老艿艿」略作修改,记录在理解过程中,参考的资料。
从这篇博客开始我们开始加载 Bean 的第一个步骤,从缓存中获取 Bean 。代码片段如下:
// AbstractBeanFactory.java |
调用 #getSingleton(String beanName)
方法,从缓存中获取 Bean 。
1. getSingleton
在上篇博客 《【死磕 Spring】—— IoC 之开启 Bean 的加载》 中提到过,Spring 对单例模式的 bean 只会创建一次。后续,如果再获取该 Bean ,则是直接从单例缓存中获取,该过程就体现在 #getSingleton(String beanName)
方法中。代码如下:
// DefaultSingletonBeanRegistry.java |
这段代码非常简单,保持淡定,过程如下:
- 第一步,从
singletonObjects
中,获取 Bean 对象。 - 第二步,若为空且当前 bean 正在创建中,则从
earlySingletonObjects
中获取 Bean 对象。 - 第三步,若为空且允许提前创建,则从
singletonFactories
中获取相应的 ObjectFactory 对象。若不为空,则调用其ObjectFactory#getObject(String name)
方法,创建 Bean 对象,然后将其加入到earlySingletonObjects
,然后从singletonFactories
删除。 总体逻辑,就是根据
beanName
依次检测这三个 Map,若为空,从下一个,否则返回。这三个 Map 存放的都有各自的功能,代码如下:// 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.
*
* 存放的是 ObjectFactory,可以理解为创建单例 bean 的 factory 。
*
* 对应关系是 bean name --> ObjectFactory
**/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/**
* Cache of early singleton objects: bean name to bean instance.
*
* 存放的是早期的 bean,对应关系也是 bean name --> bean instance。
*
* 它与 {@link #singletonFactories} 区别在于 earlySingletonObjects 中存放的 bean 不一定是完整。
*
* 从 {@link #getSingleton(String)} 方法中,我们可以了解,bean 在创建过程中就已经加入到 earlySingletonObjects 中了。
* 所以当在 bean 的创建过程中,就可以通过 getBean() 方法获取。
*
* 这个 Map 也是【循环依赖】的关键所在。
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
1.1 isSingletonCurrentlyInCreation
在上面代码中,还有一个非常重要的检测方法 #isSingletonCurrentlyInCreation(String beanName)
方法,该方法用于判断该 beanName
对应的 Bean 是否在创建过程中,注意这个过程讲的是整个工厂中。代码如下:
// DefaultSingletonBeanRegistry.java |
从这段代码中,我们可以预测,在 Bean 创建过程中都会将其加入到 singletonsCurrentlyInCreation
集合中。具体是在什么时候加的,我们后面分析。
2. getObjectForBeanInstance
到这里从缓存中获取 bean 的过程已经分析完毕了,我们再看开篇的代码段,从缓存中获取 Bean 后,若其不为 null
且 args
为空,则会调用 #getObjectForBeanInstance(Object beanInstance, String name, String beanName,RootBeanDefinition mbd)
方法,进行处理。
- 为什么会有这么一段呢?因为我们从缓存中获取的 bean 是最原始的 Bean ,并不一定使我们最终想要的 Bean 。
- 怎么办呢?调用
#getObjectForBeanInstance(...)
方法,进行处理,该方法的定义为获取给定 Bean 实例的对象,该对象要么是 bean 实例本身,要么就是 FactoryBean 创建的 Bean 对象。
代码如下:
// AbstractBeanFactory.java |
该方法主要是进行检测工作的,主要如下:
<1>
处,若name
为工厂相关的(以 & 开头),且beanInstance
为 NullBean 类型则直接返回,如果beanInstance
不为 FactoryBean 类型则抛出 BeanIsNotAFactoryException 异常。这里主要是校验beanInstance
的正确性。<2>
处,如果beanInstance
不为 FactoryBean 类型或者name
也不是与工厂相关的,则直接返回beanInstance
这个 Bean 对象。这里主要是对非 FactoryBean 类型处理。<3>
处,如果 BeanDefinition 为空,则从factoryBeanObjectCache
中加载 Bean 对象。如果还是空,则可以断定beanInstance
一定是 FactoryBean 类型,则委托#getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess)
方法,进行处理,使用 FactoryBean 获得 Bean 对象。老艿艿:所以实际上,
#getObjectForBeanInstance(...)
方法的重心,就是使用 FactoryBean 对象,获得( 或者创建 )其 Bean 对象,即调用#getObjectFromFactoryBean(...)
方法。
2.1 getObjectFromFactoryBean
从上面可以看出, #getObjectForBeanInstance(Object beanInstance, String name, String beanName,RootBeanDefinition mbd)
方法,分成两种情况:
- 第一种,当该实例对象为非 FactoryBean 类型,直接返回给定的 Bean 实例对象
beanInstance
。 - 第二种,当该实例对象为FactoryBean 类型,从 FactoryBean (
beanInstance
) 中,获取 Bean 实例对象。
第二种,通过 #getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess)
方法来实现。代码如下:
// FactoryBeanRegistrySupport.java |
主要流程如下:
- 若为单例且单例 Bean 缓存中存在
beanName
,则<1>
进行后续处理(跳转到下一步),否则,则<2>
从 FactoryBean 中获取 Bean 实例对象。 <1.1>
首先,获取锁。其实我们在前面篇幅中发现了大量的同步锁,锁住的对象都是this.singletonObjects
,主要是因为在单例模式中必须要保证全局唯一。代码如下:// 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);
public final Object getSingletonMutex() {
return this.singletonObjects;
}<1.2>
然后,从factoryBeanObjectCache
缓存中获取实例对象object
。若object
为空,则调用#doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName)
方法,从 FactoryBean 获取对象,其实内部就是调用FactoryBean#getObject()
方法。代码如下:private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
throws BeanCreationException {
Object object;
try {
// 需要权限验证
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
// <x> 从 FactoryBean 中,获得 Bean 对象
object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
} catch (PrivilegedActionException pae) {
throw pae.getException();
}
} else {
// <x> 从 FactoryBean 中,获得 Bean 对象
object = factory.getObject();
}
} catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
// Do not accept a null value for a FactoryBean that's not fully
// initialized yet: Many FactoryBeans just return null then.
if (object == null) {
if (isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(
beanName, "FactoryBean which is currently in creation returned null from getObject");
}
object = new NullBean();
}
return object;
}- 在
<x>
处,可以看到,调用FactoryBean#getObject()
方法,获取 Bean 对象。
- 在
<1.3>
如果需要后续处理(shouldPostProcess = true
),则进行进一步处理,步骤如下:- 若该 Bean 处于创建中(
#isSingletonCurrentlyInCreation(String beanName)
方法返回true
),则返回非处理的 Bean 对象,而不是存储它。 - 调用
#beforeSingletonCreation(String beanName)
方法,进行创建之前的处理。默认实现将该 Bean 标志为当前创建的。 - 调用
#postProcessObjectFromFactoryBean(Object object, String beanName)
方法,对从 FactoryBean 获取的 Bean 实例对象进行后置处理。详细解析,见 「2.3 postProcessObjectFromFactoryBean」 。 - 调用
#afterSingletonCreation(String beanName)
方法,进行创建 Bean 之后的处理,默认实现是将该 bean 标记为不再在创建中。
- 若该 Bean 处于创建中(
<1.4>
最后,加入到factoryBeanObjectCache
缓存中。
该方法应该就是创建 Bean 实例对象中的核心方法之一了。这里我们关注三个方法:
#beforeSingletonCreation(String beanName)
#afterSingletonCreation(String beanName)
#postProcessObjectFromFactoryBean(Object object, String beanName)
2.2 isSingletonCurrentlyInCreation
可能有小伙伴觉得前面两个方法不是很重要,LZ 可以肯定告诉你,这两方法是非常重要的操作,因为他们记录着 Bean 的加载状态,是检测当前 Bean 是否处于创建中的关键之处,对解决 Bean 循环依赖起着关键作用。
#beforeSingletonCreation(String beanName)
方法,用于添加标志,当前 bean 正处于创建中#afterSingletonCreation(String beanName)
方法,用于移除标记,当前 Bean 不处于创建中。
其实在这篇博客刚刚开始就已经提到了, #isSingletonCurrentlyInCreation(String beanName)
方法,是用于检测当前 Bean 是否处于创建之中。代码如下:
// DefaultSingletonBeanRegistry.java |
- 是根据
singletonsCurrentlyInCreation
集合中是否包含了beanName
。
2.2.1 beforeSingletonCreation
集合的元素,则一定是在 #beforeSingletonCreation(String beanName)
方法中添加的。代码如下:
// DefaultSingletonBeanRegistry.java |
2.2.2 afterSingletonCreation
#afterSingletonCreation(String beanName)
方法,为移除,则一定就是对 singletonsCurrentlyInCreation
集合 remove 了。代码如下:
// DefaultSingletonBeanRegistry.java |
2.3 postProcessObjectFromFactoryBean
postProcessObjectFromFactoryBean(Object object, String beanName)
方法,对从 FactoryBean 处获取的 Bean 实例对象进行后置处理。其默认实现是直接返回 object 对象,不做任何处理。代码如下:
// DefaultSingletonBeanRegistry.java |
2.3.1
当然,子类可以重写,例如应用后处理器。org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
抽象类,对其提供了实现,代码如下:
// AbstractAutowireCapableBeanFactory.java |
该方法的定义为:对所有的
{@code postProcessAfterInitialization}
进行回调注册 BeanPostProcessors ,让他们能够后期处理从 FactoryBean 中获取的对象。下面是具体实现:// AbstractAutowireCapableBeanFactory.java
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
// 遍历 BeanPostProcessor
for (BeanPostProcessor processor : getBeanPostProcessors()) {
// 处理
Object current = processor.postProcessAfterInitialization(result, beanName);
// 返回空,则返回 result
if (current == null) {
return result;
}
// 修改 result
result = current;
}
return result;
}- 对于后置处理器,这里我们不做过多阐述,后面会专门的博文进行详细介绍,这里我们只需要记住一点:尽可能保证所有 bean 初始化后都会调用注册的
BeanPostProcessor#postProcessAfterInitialization(Object bean, String beanName)
方法进行处理,在实际开发过程中大可以针对此特性设计自己的业务逻辑。
- 对于后置处理器,这里我们不做过多阐述,后面会专门的博文进行详细介绍,这里我们只需要记住一点:尽可能保证所有 bean 初始化后都会调用注册的
3. 小结
至此,从缓存中获取 Bean 对象过程已经分析完毕了。
下面两篇博客分析,如果从单例缓存中没有获取到单例 Bean ,则 Spring 是如何处理的。