javascript
Spring IOC 容器源码分析 - 创建单例 bean 的过程
1. 簡介
在上一篇文章中,我比較詳細的分析了獲取 bean 的方法,也就是getBean(String)的實現邏輯。對于已實例化好的單例 bean,getBean(String) 方法并不會再一次去創建,而是從緩存中獲取。如果某個 bean 還未實例化,這個時候就無法命中緩存。此時,就要根據 bean 的配置信息去創建這個 bean 了。相較于getBean(String)方法的實現邏輯,創建 bean 的方法createBean(String, RootBeanDefinition, Object[])及其所調用的方法邏輯上更為復雜一些。關于創建 bean 實例的過程,我將會分幾篇文章進行分析。本篇文章會先從大體上分析?createBean(String, RootBeanDefinition, Object[])方法的代碼邏輯,至于其所調用的方法將會在隨后的文章中進行分析。
好了,其他的不多說,直接進入正題吧。
?2. 源碼分析
?2.1 創建 bean 實例的入口
在正式分析createBean(String, RootBeanDefinition, Object[])方法前,我們先來看看 createBean 方法是在哪里被調用的。如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public T doGetBean(...) {// 省略不相關代碼if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {destroySingleton(beanName);throw ex;}}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}// 省略不相關代碼 } |
上面是 doGetBean 方法的代碼片段,從中可以發現 createBean 方法。createBean 方法被匿名工廠類的 getObject 方法包裹,但這個匿名工廠類對象并未直接調用 getObject 方法。而是將自身作為參數傳給了getSingleton(String, ObjectFactory)方法,那么我們接下來再去看看一下getSingleton(String, ObjectFactory) 方法的實現。如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "'beanName' must not be null");synchronized (this.singletonObjects) {// 從緩存中獲取單例 bean,若不為空,則直接返回,不用再初始化Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {if (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName,"Singleton bean creation not allowed while singletons of this factory are in destruction " +"(Do not request a bean from a BeanFactory in a destroy method implementation!)");}if (logger.isDebugEnabled()) {logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}/* * 將 beanName 添加到 singletonsCurrentlyInCreation 集合中,* 用于表明 beanName 對應的 bean 正在創建中*/beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet<Exception>();}try {// 通過 getObject 方法調用 createBean 方法創建 bean 實例singletonObject = singletonFactory.getObject();newSingleton = true;}catch (IllegalStateException ex) {singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {throw ex;}}catch (BeanCreationException ex) {if (recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {ex.addRelatedCause(suppressedException);}}throw ex;}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}// 將 beanName 從 singletonsCurrentlyInCreation 移除afterSingletonCreation(beanName);}if (newSingleton) {/* * 將 <beanName, singletonObject> 鍵值對添加到 singletonObjects 集合中,* 并從其他集合(比如 earlySingletonObjects)中移除 singletonObject 記錄*/addSingleton(beanName, singletonObject);}}return (singletonObject != NULL_OBJECT ? singletonObject : null);} } |
上面的方法邏輯不是很復雜,這里簡單總結一下。如下:
從上面的分析中,我們知道了 createBean 方法在何處被調用的。那么接下來我們一起深入 createBean 方法的源碼中,來看看這個方法具體都做了什么事情。
?2.2 createBean 方法全貌
createBean 和 getBean 方法類似,基本上都是空殼方法,只不過 createBean 的邏輯稍微多點,多做了一些事情。下面我們一起看看這個方法的實現邏輯,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {if (logger.isDebugEnabled()) {logger.debug("Creating instance of bean '" + beanName + "'");}RootBeanDefinition mbdToUse = mbd;// 解析 bean 的類型Class<?> resolvedClass = resolveBeanClass(mbd, beanName);if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {mbdToUse = new RootBeanDefinition(mbd);mbdToUse.setBeanClass(resolvedClass);}try {// 處理 lookup-method 和 replace-method 配置,Spring 將這兩個配置統稱為 override methodmbdToUse.prepareMethodOverrides();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),beanName, "Validation of method overrides failed", ex);}try {// 在 bean 初始化前應用后置處理,如果后置處理返回的 bean 不為空,則直接返回Object bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {return bean;}}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,"BeanPostProcessor before instantiation of bean failed", ex);}// 調用 doCreateBean 創建 beanObject beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isDebugEnabled()) {logger.debug("Finished creating instance of bean '" + beanName + "'");}return beanInstance; } |
上面的代碼不長,代碼的執行流程比較容易看出,這里羅列一下:
下面我會分節對第2、3和4步的流程進行分析,步驟1的詳細實現大家有興趣的話,就自己去看看吧。
?2.2.1 驗證和準備 override 方法
當用戶配置了 lookup-method 和 replace-method 時,Spring 需要對目標 bean 進行增強。在增強之前,需要做一些準備工作,也就是 prepareMethodOverrides 中的邏輯。下面來看看這個方法的源碼:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | public void prepareMethodOverrides() throws BeanDefinitionValidationException {MethodOverrides methodOverrides = getMethodOverrides();if (!methodOverrides.isEmpty()) {Set<MethodOverride> overrides = methodOverrides.getOverrides();synchronized (overrides) {// 循環處理每個 MethodOverride 對象for (MethodOverride mo : overrides) {prepareMethodOverride(mo);}}} }protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {// 獲取方法名為 mo.getMethodName() 的方法數量,當方法重載時,count 的值就會大于1int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());// count = 0,表明根據方法名未找到相應的方法,此時拋出異常if (count == 0) {throw new BeanDefinitionValidationException("Invalid method override: no method with name '" + mo.getMethodName() +"' on class [" + getBeanClassName() + "]");}// 若 count = 1,表明僅存在已方法名為 mo.getMethodName(),這意味著方法不存在重載else if (count == 1) {// 方法不存在重載,則將 overloaded 成員變量設為 falsemo.setOverloaded(false);} } |
上面的源碼中,prepareMethodOverrides方法循環調用了prepareMethodOverride方法,并沒其他的太多邏輯。主要準備工作都是在 prepareMethodOverride 方法中進行的,所以我們重點關注一下這個方法。prepareMethodOverride 這個方法主要用于獲取指定方法的方法數量 count,并根據 count 的值進行相應的處理。count = 0 時,表明方法不存在,此時拋出異常。count = 1 時,設置 MethodOverride 對象的overloaded成員變量為 false。這樣做的目的在于,提前標注名稱mo.getMethodName()的方法不存在重載,在使用 CGLIB 增強階段就不需要進行校驗,直接找到某個方法進行增強即可。
上面的方法沒太多的邏輯,比較簡單,就先分析到這里。
?2.2.2 bean 實例化前的后置處理
后置處理是 Spring 的一個拓展點,用戶通過實現 BeanPostProcessor 接口,并將實現類配置到 Spring 的配置文件中(或者使用注解),即可在 bean 初始化前后進行自定義操作。關于后置處理較為詳細的說明,可以參考我的了一篇文章Spring IOC 容器源碼分析系列文章導讀,這里就不贅述了。下面我們來看看 createBean 方法中的后置處理邏輯,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {Object bean = null;// 檢測是否解析過,mbd.beforeInstantiationResolved 的值在下面的代碼中會被設置if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {Class<?> targetType = determineTargetType(beanName, mbd);if (targetType != null) {// 應用前置處理bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);if (bean != null) {// 應用后置處理bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);}}}// 設置 mbd.beforeInstantiationResolvedmbd.beforeInstantiationResolved = (bean != null);}return bean; }protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {for (BeanPostProcessor bp : getBeanPostProcessors()) {// InstantiationAwareBeanPostProcessor 一般在 Spring 框架內部使用,不建議用戶直接使用if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;// bean 初始化前置處理Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);if (result != null) {return result;}}}return null; }public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {// bean 初始化后置處理result = beanProcessor.postProcessAfterInitialization(result, beanName);if (result == null) {return result;}}return result; } |
在 resolveBeforeInstantiation 方法中,當前置處理方法返回的 bean 不為空時,后置處理才會被執行。前置處理器是 InstantiationAwareBeanPostProcessor 類型的,該種類型的處理器一般用在 Spring 框架內部,比如 AOP 模塊中的AbstractAutoProxyCreator抽象類間接實現了這個接口中的?postProcessBeforeInstantiation方法,所以 AOP 可以在這個方法中生成為目標類的代理對象。不過我在調試的過程中,發現 AOP 在此處生成代理對象是有條件的。一般情況下條件都不成立,也就不會在此處生成代理對象。至于這個條件為什么不成立,因 AOP 這一塊的源碼我還沒來得及看,所以暫時還無法解答。等我看過 AOP 模塊的源碼后,我再來嘗試分析這個條件。
?2.2.3 調用 doCreateBean 方法創建 bean
這一節,我們來分析一下doCreateBean方法的源碼。在 Spring 中,做事情的方法基本上都是以do開頭的,doCreateBean 也不例外。那下面我們就來看看這個方法都做了哪些事情。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)throws BeanCreationException {/* * BeanWrapper 是一個基礎接口,由接口名可看出這個接口的實現類用于包裹 bean 實例。* 通過 BeanWrapper 的實現類可以方便的設置/獲取 bean 實例的屬性*/BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {// 從緩存中獲取 BeanWrapper,并清理相關記錄instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {/* * 創建 bean 實例,并將實例包裹在 BeanWrapper 實現類對象中返回。createBeanInstance * 中包含三種創建 bean 實例的方式:* 1. 通過工廠方法創建 bean 實例* 2. 通過構造方法自動注入(autowire by constructor)的方式創建 bean 實例* 3. 通過無參構造方法方法創建 bean 實例** 若 bean 的配置信息中配置了 lookup-method 和 replace-method,則會使用 CGLIB * 增強 bean 實例。關于這個方法,后面會專門寫一篇文章介紹,這里先說這么多。*/instanceWrapper = createBeanInstance(beanName, mbd, args);}// 此處的 bean 可以認為是一個原始的 bean 實例,暫未填充屬性final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);mbd.resolvedTargetType = beanType;// 這里又遇到后置處理了,此處的后置處理是用于處理已“合并的 BeanDefinition”。關于這種后置處理器具體的實現細節就不深入理解了,大家有興趣可以自己去看synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}/** earlySingletonExposure 是一個重要的變量,這里要說明一下。該變量用于表示是否提前暴露* 單例 bean,用于解決循環依賴。earlySingletonExposure 由三個條件綜合而成,如下:* 條件1:mbd.isSingleton() - 表示 bean 是否是單例類型* 條件2:allowCircularReferences - 是否允許循環依賴* 條件3:isSingletonCurrentlyInCreation(beanName) - 當前 bean 是否處于創建的狀態中* * earlySingletonExposure = 條件1 && 條件2 && 條件3 * = 單例 && 是否允許循環依賴 && 是否存于創建狀態中。*/boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isDebugEnabled()) {logger.debug("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}// 添加工廠對象到 singletonFactories 緩存中addSingletonFactory(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {// 獲取早期 bean 的引用,如果 bean 中的方法被 AOP 切點所匹配到,此時 AOP 相關邏輯會介入return getEarlyBeanReference(beanName, mbd, bean);}});}Object exposedObject = bean;try {// 向 bean 實例中填充屬性,populateBean 方法也是一個很重要的方法,后面會專門寫文章分析populateBean(beanName, mbd, instanceWrapper);if (exposedObject != null) {/** 進行余下的初始化工作,詳細如下:* 1. 判斷 bean 是否實現了 BeanNameAware、BeanFactoryAware、* BeanClassLoaderAware 等接口,并執行接口方法* 2. 應用 bean 初始化前置操作* 3. 如果 bean 實現了 InitializingBean 接口,則執行 afterPropertiesSet * 方法。如果用戶配置了 init-method,則調用相關方法執行自定義初始化邏輯* 4. 應用 bean 初始化后置操作* * 另外,AOP 相關邏輯也會在該方法中織入切面邏輯,此時的 exposedObject 就變成了* 一個代理對象了*/exposedObject = initializeBean(beanName, exposedObject, mbd);}}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {// 若 initializeBean 方法未改變 exposedObject 的引用,則此處的條件為 true。if (exposedObject == bean) {exposedObject = earlySingletonReference;}// 下面的邏輯我也沒完全搞懂,就不分析了。見諒。else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] in its raw version as part of a circular reference, but has eventually been " +"wrapped. This means that said other beans do not use the final version of the " +"bean. This is often the result of over-eager type matching - consider using " +"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");}}}}try {// 注冊銷毀邏輯registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject; } |
上面的注釋比較多,分析的應該比較詳細的。不過有一部分代碼我暫時沒看懂,就不分析了,見諒。下面我們來總結一下 doCreateBean 方法的執行流程吧,如下:
doCreateBean 方法的流程比較復雜,步驟略多。由此也可了解到創建一個 bean 還是很復雜的,這中間要做的事情繁多。比如填充屬性、對 BeanPostProcessor 拓展點提供支持等。以上的步驟對應的方法具體是怎樣實現的,本篇文章并不打算展開分析。在后續的文章中,我會單獨寫文章分析幾個邏輯比較復雜的步驟。有興趣的閱讀的朋友可以稍微等待一下,相關文章本周會陸續進行更新。
?3. 總結
到這里,createBean 方法及其所調用的方法的源碼就分析完了。總的來說,createBean 方法還是比較復雜的,需要多看幾遍才能理清一些頭緒。由于 createBean 方法比較復雜,對于以上的源碼分析,我并不能保證不出錯。如果有寫錯的地方,還請大家指點迷津。畢竟當局者迷,作為作者,我很難意識到哪里寫的有問題。
好了,本篇文章到此結束。謝謝閱讀。
?參考
- 《Spring 源碼深度解析》- 郝佳
?附錄:Spring 源碼分析文章列表
?Ⅰ. IOC
| 2018-05-30 | Spring IOC 容器源碼分析系列文章導讀 |
| 2018-06-01 | Spring IOC 容器源碼分析 - 獲取單例 bean |
| 2018-06-04 | Spring IOC 容器源碼分析 - 創建單例 bean 的過程 |
| 2018-06-06 | Spring IOC 容器源碼分析 - 創建原始 bean 對象 |
| 2018-06-08 | Spring IOC 容器源碼分析 - 循環依賴的解決辦法 |
| 2018-06-11 | Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對象 |
| 2018-06-11 | Spring IOC 容器源碼分析 - 余下的初始化工作 |
?Ⅱ. AOP
| 2018-06-17 | Spring AOP 源碼分析系列文章導讀 |
| 2018-06-20 | Spring AOP 源碼分析 - 篩選合適的通知器 |
| 2018-06-20 | Spring AOP 源碼分析 - 創建代理對象 |
| 2018-06-22 | Spring AOP 源碼分析 - 攔截器鏈的執行過程 |
?Ⅲ. MVC
| 2018-06-29 | Spring MVC 原理探秘 - 一個請求的旅行過程 |
| 2018-06-30 | Spring MVC 原理探秘 - 容器的創建過程 |
- 本文鏈接:?https://www.tianxiaobo.com/2018/06/04/Spring-IOC-容器源碼分析-創建單例-bean/
from:?http://www.tianxiaobo.com/2018/06/04/Spring-IOC-%E5%AE%B9%E5%99%A8%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E5%88%9B%E5%BB%BA%E5%8D%95%E4%BE%8B-bean/?
總結
以上是生活随笔為你收集整理的Spring IOC 容器源码分析 - 创建单例 bean 的过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring IOC 容器源码分析 -
- 下一篇: Spring IOC 容器源码分析 -