讀完這篇文章你將會收穫到web
getBean
方法中,
Spring
處理別名以及
factoryBean
的
name
Spring
如何從多級緩存中根據
beanName
獲取
bean
Spring
如何處理用戶獲取普通
bean
和
factoryBean
從 Spring 容器的初始化 中,咱們瞭解到 Spring
是如何將 XML
文件轉換爲 BeanDefinition
並註冊到 BeanDefinitionRegstry
。spring
今天咱們一塊兒繼續學習 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 對象,固然咱們可使用默認的別名獲取app
System.out.println(defaultListableBeanFactory.getBean("com.demo.data.Person"));
複製代碼
對 Spring 別名不熟悉的朋友能夠先看下個人這一篇文章 Spring-AliasRegistry編輯器
咱們直接進入到 AbstractBeanFactory#getBean(String)
方法中, AbstractBeanFactory
爲 DefaultListableBeanFactory
的父類ide
@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); } .......... ........... 複製代碼
由於這個方法實在太長了因此截取一部分、咱們一步步來分析post
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 用來解決某些場景下的循環依賴
咱們再回到代碼中、分析一下它的邏輯
相似多級緩存的設計
在上面的方法中咱們看到 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 、若是是的話就直接返回不是的話就要拋出異常了
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 裏面的這三個方法
singletonObjects
中看看有沒有
earlySingletonObjects
singletonFactories
中看看有沒有
&
)、那麼咱們直接返回
BeanPostProcessor
上面的三個方法大體流程就是這樣、但願對各位有幫助
有興趣進入羣聊、一塊兒交流一塊兒划水