讀完這篇文章你將會收穫到
- 在
getBean
方法中,Spring
處理別名以及factoryBean
的name
Spring
如何從多級緩存中根據beanName
獲取bean
Spring
如何處理用戶獲取普通bean
和factoryBean
引言
從 Spring 容器的初始化 中,我們瞭解到 Spring
是如何將 XML
文件轉換爲 BeanDefinition
並註冊到 BeanDefinitionRegstry
。
今天我們一起繼續學習 Spring
的 bean
加載
public static void main(String[] args) {
Resource resource = new ClassPathResource("coderLi.xml");
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
xmlBeanDefinitionReader.loadBeanDefinitions(resource);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean class="com.demo.data.Person">
<description>
微信搜一搜:CoderLi
</description>
</bean>
</beans>
熟悉的味道、熟悉的配方
源碼分析
我們可以在上面的 Java 代碼中加入以下的代碼
System.out.println(defaultListableBeanFactory.getBean("com.demo.data.Person#0"));
我們根據默認的 beanName 從 Spring 容器中獲取 Person 這個 bean 對象,當然我們可以使用默認的別名獲取
System.out.println(defaultListableBeanFactory.getBean("com.demo.data.Person"));
對 Spring 別名不熟悉的朋友可以先看下我的這一篇文章 Spring-AliasRegistry
我們直接進入到 AbstractBeanFactory#getBean(String)
方法中, AbstractBeanFactory
爲 DefaultListableBeanFactory
的父類
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
可以看到 do
開頭的纔是真正幹活的老大 , AbstractBeanFactory#doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 找到這個參數的 bean name
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 檢查緩衝中是否有這個bean、spring中只是保存單例的bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 這裏被我刪除了一些spring 的log
// 處理一下 factory bean 的情況、包括從 factory beans 的緩存中獲取、或者重新調用 factory bean 的 get bean 方法 包括一些回調
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
..........
...........
因爲這個方法實在太長了所以截取一部分、我們一步步來分析
transformedBeanName(name)
這個方法就是將我們的 name 轉換爲真正的 beanName,因爲我們傳進來的參數可能是一個 alias 或者可能是一個 factoryBean 的 beanName (前綴爲&),而我們在 Spring 中存放的 factoryBean 的 beanName 是沒有 & 前綴的,所以需要處理掉這個前綴
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
// 是否是一個 factory bean 、如果不是的話就直接返回
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
return name;
}
// 如果是的話就將其前綴 & 去掉
return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
do {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
});
}
/**
* 找到這個別名的最終的 bean Name、如果沒有的話(
* 也就是說參數中的name 就是人家的 bean name 那麼就直接返回這個 參數就行了)
*
*/
public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
String resolvedName;
do {
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}
讓我們再看看下一個方法 DefaultSingletonBeanRegistry#getSingleton(String)
public Object getSingleton(String beanName) {
// allowEarlyReference 允許早期依賴
return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
// 這個bean 正處於 創建階段
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 併發控制
synchronized (this.singletonObjects) {
// 單例緩存是否存在
singletonObject = this.earlySingletonObjects.get(beanName);
// 是否運行獲取 bean factory 創建出的 bean
if (singletonObject == null && allowEarlyReference) {
// 獲取緩存中的 ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 將對象緩存到 earlySingletonObject中
this.earlySingletonObjects.put(beanName, singletonObject);
// 從工廠緩衝中移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
上面的代碼就是 Spring 嘗試從緩存中加載單例。單例在 Spring 的同一個容器中只會被創建一次,後續再獲取 bean,就直接從緩存中取了。
在介紹這個方法之前、我們先認識下 DefaultSingletonBeanRegistry
這個類裏面的成員變量吧
Map<String, Object> singletonObjects
這個很好理解、 key 就是 beanName ,value 就是 bean 實例Map<String, ObjectFactory<?>> singletonFactories
key 爲 beanName,value 爲創建 bean 的工廠Map<String, Object> earlySingletonObjects
key 爲 beanName ,value 爲 bean。但是和singletonObjects
不同的是,bean 被加入到earlySingletonObjects
的時候、這個 bean 還是處於一種創建中的狀態,目的也很簡單、Spring 用來解決某些場景下的循環依賴
我們再回到代碼中、分析一下它的邏輯
- 先從 singletonObjects 中嘗試獲取 bean,這裏存放的是已經創建好的 bean 了、如果在這裏能知道、那當然是最好啦
- 如果在這裏找不到的話、那麼我們就要判斷下這個 beanName 對應的 bean 是否正在創建中
- 如果是的話,那麼我們再看看這個正在創建的 bean 是否已經曝光出來、如果沒有的話、那麼就要看看我們的參數是否允許依賴早期的 bean 了、
- 如果允許早期依賴、那麼我們就嘗試衝 ObjectFactory 中獲取到對應的 bean、並將它放入到 earlySingletonObjects 中、並從 singletonFactories 中移除
類似多級緩存的設計
在上面的方法中我們看到 isSingletonCurrentlyInCreation(beanName)
這個方法、
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
singletonsCurrentlyInCreation
這個 Set 中,當創建一個 bean 之前會將其 對應的 beanName 放置到這個 Set 中、後面的分析會涉及到、這裏先提一嘴
我們第一次獲取這個 bean 、返回爲 null 是正常的
那假如我們在代碼中 getBean 了兩次
defaultListableBeanFactory.getBean("com.demo.data.Person#0")
defaultListableBeanFactory.getBean("com.demo.data.Person#0")
那麼針對第二次的調用、返回的值就不是爲 null 了
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 處理一下 factory bean 的情況、包括從 factory beans 的緩存中獲取、或者重新調用 factory bean 的 get bean 方法 包括一些回調
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
我們先假設 sharedInstance 不爲 null 也就是我們第二次調用 getBean
,我們進入到 getObjectForBeanInstance
方法中
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// 我王大錘就是想要一個 factory bean
if (BeanFactoryUtils.isFactoryDereference(name)) {
// 如果這個是 NullBean 類型、表示這是一個 null 的 instance、直接返回
if (beanInstance instanceof NullBean) {
return beanInstance;
}
// 獲取到的 beanInstance 不是一個 factory、但是你tm name 又帶有這個 & 很迷惑啊兄弟
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
}
// 王大錘不想要factory bean、並且spring 也幫他找到了一個普通的 bean、直接返回
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
// 王大錘要的是一個普通的bean 、但是spring 給他找到了一個 factory的bean、那麼spring 是不是要做一些額外的處理 給王大錘返回一個普通的bean
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
} else {
// 從緩存中 看看有沒有
object = getCachedObjectForFactoryBean(beanName);
}
// 如果 bean factory 中還是沒有
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// 從 緩存中拿到 bean definition
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
// 是否是用戶定義的還是程序本身需要創建的bean
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
我們按步驟分析下上面的代碼
- 我們調用
getBean(name)
中的 name 如果包含前綴 & ,表面我們是想要從 Spring 中獲取一個 FactoryBean ,那麼我們就要判斷我們從緩存中獲取的 beanInstance 是否是 一個 FactoryBean 、如果是的話就直接返回不是的話就要拋出異常了 - 我們想要的是一個非 factoryBean 並且 在 spring 容器中找到了非 factoryBean 的 bean、那麼就直接返回
- 我們想要的是一個 非 factoryBean 但是在 spring 容器中找到了一個 factoryBean 的 bean、那麼就要進入到
getObjectFromFactoryBean
方法中了
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
// 爲單例模式且緩存中存在
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
// 從緩存中獲取指定的 bean(這個bean 是從 factory bean 創建出來的)
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 爲空則從 factory bean 中獲取對象
object = doGetObjectFromFactoryBean(factory, beanName);
// 從緩存中獲取
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
// 已經存放到 緩存中了、後續的操作就不需要了
object = alreadyThere;
} else {
// 需要做一些後置處理
if (shouldPostProcess) {
// 如果這個bean正在創建中、
if (isSingletonCurrentlyInCreation(beanName)) {
return object;
}
// 前置處理 主要是將這個bean 加入到正在創建中的隊列 singletonsCurrentlyInCreation
beforeSingletonCreation(beanName);
try {
// 對 從 factoryBean 獲取的對象進行後處理
// 生成對象將暴露給 bean 引用 並回調 beanPostProcessor
object = postProcessObjectFromFactoryBean(object, beanName);
} catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
} finally {
// 後置處理 將其從 singletonsCurrentlyInCreation 移除
afterSingletonCreation(beanName);
}
}
// 他的 factory bean 已經存在 緩存中了、那麼這個 factory bean 產生的bean 應該也要緩存一下
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;
}
}
啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊、代碼很長長長…我們一點點來分析
-
第一步就是判斷這個 factoryBean 是否是單例、如果不是的話,並且是用戶自己定義的 bean、那麼就需要調用
postProcessObjectFromFactoryBean
方法去做一個後續的處理- 這裏面最終回調的就是我們常用的一個接口
BeanPostProcessor
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }
- 這裏面最終回調的就是我們常用的一個接口
-
如果這 beanFactory 是一個單例,那我們就看看 factoryBeanObjectCache ( key 是 beanName,value 是 beanFactory 產生出來的 object 也是我們正要獲取的 bean ) 這個 Map 中是否存在這個 beanName 這個 bean
-
如果存在的話、就直接返回、如果不存在的話、那就
doGetObjectFromFactoryBean
,從這個方法中使用 FactoryBean#getObject 產生 bean -
其實下面這段代碼確實讓人看不懂哦
Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) { // 已經存放到 緩存中了、後續的操作就不需要了 object = alreadyThere; }
-
然後我們看到
beforeSingletonCreation
這個方法、就是上面getSingleton
中isSingletonCurrentlyInCreation
判斷一個 bean 是否處於正在創建中protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } } public boolean isSingletonCurrentlyInCreation(String beanName) { return this.singletonsCurrentlyInCreation.contains(beanName); }
-
然後又調用到
postProcessObjectFromFactoryBean
方法、最終回調的就是我們常用的一個接口BeanPostProcessor
-
最好調用
afterSingletonCreation(beanName)
方法、將其從 正在創建中的 bean 的集合中移除、最後的最後、將其加入到factoryBeanObjectCache
集合中
今天我們就先分析到這裏、後續的話我們在後面的文章繼續探討。今天我們大致分析了 getBean 裏面的這三個方法
總結
- 根據參數中的 name 找出對應的 beanName、無論這個 name 是別名或者是一個 factoryBean 的 beanName
- 查看緩存中是否包含這個 beanName 對象
- 先從一級緩存
singletonObjects
中看看有沒有 - 然後從二級緩存
earlySingletonObjects
- 都沒有的話再從三級緩存
singletonFactories
中看看有沒有
- 先從一級緩存
- 如果緩存中有 bean、那麼我們還是需要處理一下這個 bean
- 如果 Spring 緩存中返回的 bean 是 factoryBean、而用戶也想要的是一個 beanFactory (參數 name 中的前綴是
&
)、那麼我們直接返回 - 如果 Spring 緩存中返回的 bean 是普通的 bean、而用戶也想要的是一個普通的 bean 、那麼就直接返回
- 如果 Spring 緩存中返回的 bean 是一個 factoryBean、而用戶想要的是一個普通的 bean 、那麼我們就要從 factoryBean 中獲取這個 bean
- 而從 factoryBean 中獲取這個 bean的過程中、需要調用到前置處理、後置處理和我們常用的接口回調
BeanPostProcessor
- 如果 Spring 緩存中返回的 bean 是 factoryBean、而用戶也想要的是一個 beanFactory (參數 name 中的前綴是
上面的三個方法大致流程就是這樣、希望對各位有幫助
有興趣進入羣聊、一起交流一起划水