这是我参与「掘金日新计划 · 8 月更文挑战」的第33天,点击查看活动详情
基于 Spring Framework v5.2.6.RELEASE
前情提要
之前的文章介绍了,在 AbstractBeanFactory 的doGetBean
方法中,完成 Bean 名称转换后,第一步就是到 Spring 容器的三级缓存中获取单例 Bean 的实例对象。
如果在容器的缓存中获取到 Bean 的实例对象,Spring 还会对这个对象进行进一步处理。这部分的代码如下:
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
复制代码
除了日志处理的部分,主要是调用了getObjectForBeanInstance
方法获取到了最终作为结果返回的 Bean 实例。
在从缓存中获取到 Bean 的实例对象之后,Spring 还需要做什么后续工作,本文就以这个方法为切入点,进行深入分析。
处理不同类型的 Bean
首先,需要注意以下调用getObjectForBeanInstance
方法时的参数,这里包括以下内容:
- 从缓存中获取到的 Bean 实例对象
sharedInstance
,根据前面的if判断,此时这个对象不为空。 - 调用
doGetBean
方法的参数name
,也就是进行 Bean 名称转换之前的值。这个name
的值也是最开始调用getBean
方法时传入的参数值,可以说,它代表了调用getBean
方法时的意图。 - 转换之后的规范的
beanName
,也就是 Bean 在容器中的唯一标识符。 - 最后一个参数传入了
null
,之后的分析终于到了再具体看。
接下来进入方法的代码:
// org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// Don't let calling code try to dereference the factory if the bean isn't a factory.
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
}
// Now we have the bean instance, which may be a normal bean or a FactoryBean.
// If it's a FactoryBean, we use it to create a bean instance, unless the
// caller actually wants a reference to the factory.
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
复制代码
方法体中的代码,大概用空行分割成了三个部分,我们逐个来分析。
首先判断name
属性的值,是不是一个工厂引用,具体的判断方式如下:
// org.springframework.beans.factory.BeanFactoryUtils#isFactoryDereference
public static boolean isFactoryDereference(@Nullable String name) {
return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}
复制代码
很简单,就是看它是不是以&
符号开头的。再次提示,这里判断的是原始的name
参数值。
此处需要再说明一下,前文中说,这个值代表了最开始调用getBean
方法的意图,什么意思呢?
因为&
代表这个name
是一个逆向引用,如果调用getBean
方法传入的name
是以&
开头的话,说明调用getBean
方法的意图,是为了获取用来创建 Bean 的 FactoryBean 的实例,如果不是以&
开头的话,说明调用getBean
方法的意图,是为了获取 Bean 的实例对象本身。
上面一段话读三遍,后面要考。
回到代码第一部分的逻辑,如果name
是以&
符号开头,表明我们要获取的是 FactoryBean 本身的实例。首先判断了,如果beanInstance
是一个 NullBean 就直接返回。之后,判断了如果它不是 FactoryBean 的实例,就会报错,没问题的话,则返回。
这里报错是因为,缓存中获取的实例beanInstance
不是一个 FactoryBean 的实例,name
中包含了&
符号代表了方法调用的意图是要获取 FactoryBean 本身的实例。
如果name
不是以&
符号开头,那么,则说明要获取的就是 Bean 的实例对象。
下面看方法体中的第二部分代码:
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
复制代码
这里很简单,如果beanInstance
不是 FactoryBean 的实例,那么,它就是要获取的对象实例本身,所以直接返回就可以了。
之后就剩下最后一种情况,beanInstance
是 FactoryBean 的实例,且name
不以&
开头。也就是说,当前已经获取到的beanInstance
是创建 Bean 的工厂实例,但是要获取的是工厂创建出来的 Bean 对象实例。
以下是处理这种情况的代码:
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
复制代码
根据调用方法时的参数值,这里的mbd
一开始是null
。因此,首先会通过getCachedObjectForFactoryBean
方法获取。
// org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getCachedObjectForFactoryBean
@Nullable
protected Object getCachedObjectForFactoryBean(String beanName) {
return this.factoryBeanObjectCache.get(beanName);
}
复制代码
这里又出现了一个容器,我们看一下它的定义:
/** Cache of singleton objects created by FactoryBeans: FactoryBean name to object. */
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);
复制代码
根据注释可知,它用来缓存被 FactoryBean 创建好的单例对象。
如果从这个容器中获取到的对象不为空,则它就是最后的结果。如果为空,那么进入下一个if
语句块。在这个语句块中,首先将beanInstance
强制转换为 FactoryBean 的类型,然后判断容器中是否有beanName
对应的 BeanDefinition,有的话将其获取到。
这里我们假设 Spring 的配置及初始化过程都没问题,并且这里请求的是一个已经配置的 Bean,那么,这里的mbd
变量就是已经获取到的 BeanDinifition。之后的工作,就是开始使用这个factory
,来得到真正的 Bean 实例对象,这部分逻辑在getObjectFromFactoryBean
方法中。
在分析它之前,顺便说一下传入的参数!synthetic
。根据我们的假设,这里的mbd
不为空,且synthetic
的默认值是false
,因此这里传入的参数值是true
。
使用 FactoryBean 获取 Bean 的对象实例
进入getObjectFromFactoryBean
方法的代码:
// org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
object = doGetObjectFromFactoryBean(factory, beanName);
// Only post-process and store if not put there already during getObject() call above
// (e.g. because of circular reference processing triggered by custom getBean calls)
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
}
else {
if (shouldPostProcess) {
if (isSingletonCurrentlyInCreation(beanName)) {
// Temporarily return non-post-processed object, not storing it yet..
return object;
}
beforeSingletonCreation(beanName);
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
afterSingletonCreation(beanName);
}
}
if (containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
}
else {
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
}
复制代码
方法体比较长,我们挑关键的代码来看。
首先判断了factory是不是单例的,且单例缓存中是不是存在beanName对应的单例对象。我们只考虑单例的情况,这里判断条件的结果是true
。
接下来,会调用doGetObjectFromFactoryBean
方法,来创建 Bean 实例对象。这个方法中主要的代码就是调用了factory
的getObject
方法,这是 FactoryBean 的实现类用来创建对象的方法。创建完之后,进入下面的流程。
判断条件if (shouldPostProcess)
中,shouldPostProcess
是通过参数传递的,前面已经分析过了它的值是true
,因此,进入if
语句块的流程。
在if
语句块中,通过isSingletonCurrentlyInCreation
方法判断了实例是不是正在创建中,如果是,就直接返回。这里的判断,其实也是判断一个集合中是否包含beanName
,这个集合是singletonsCurrentlyInCreation
。假设我们是第一次创建这个实例,则会进入之后的流程,关键的代码有三行:
beforeSingletonCreation(beanName);
object = postProcessObjectFromFactoryBean(object, beanName);
afterSingletonCreation(beanName);
复制代码
这里beforeSingletonCreation
和afterSingletonCreation
方法的源码如下:
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#beforeSingletonCreation
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#afterSingletonCreation
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
复制代码
其实就是在执行postProcessObjectFromFactoryBean
方法期间,将beanName
保存在singletonsCurrentlyInCreation
集合中,与前面的判断语句对应。(另外,这里还有一个排除检查的列表inCreationCheckExclusions
)
最后再看一下postProcessObjectFromFactoryBean
方法究竟对object
做了什么处理。
protected Object postProcessObjectFromFactoryBean(Object object, String beanName) throws BeansException {
return object;
}
复制代码
其实什么也没做,因此这里应该是提供给子类的扩展点。
总结
到这里,从缓存中获取到 Bean 实例并通过getObjectForBeanInstance
方法获取最后的结果对象的过程就分析完了,getObjectForBeanInstance
方法的主要作用,是针对实现了 FactoryBean 接口的类型的 Bean 的处理。这里有一个少有人知道的知识点,就是,如果一个 Bean 的类型是 FactoryBean 的实现类,那么,如果想获取到创建 Bean 实例的 FactoryBean 本身的实例,可以通过调用getBean("&beanName")
来获取。
走完这部分的流程,这里得到的对象,就是doGetBean
方法最后要返回的对象。不过到此,我们只分析了doGetBean
方法的一小部分,也就是 Bean 的实例对象能够在缓存中获取的情况。如果在doGetBean
方法的第一步,没有在缓存中获取到 Bean 的实例,那么就需要从头初始化这个 Bean 的实例,从下一篇开始,会分析这种情况下,Bean 是如何被初始化并获取到的。