javascript
5.0:Spring-bean的加载
內(nèi)容來自<Spring深度解析>,之后的不一一復(fù)述!
在Spring中,最基本的IOC容器接口是BeanFactory - 這個(gè)接口為具體的IOC容器的實(shí)現(xiàn)作了最基本的功能規(guī)定 - 不管怎么著,作為IOC容器,這些接口你必須要滿足應(yīng)用程序的最基本要求:?
對bean加載的探索。bean加載的功能實(shí)現(xiàn)遠(yuǎn)比bean的解析要復(fù)雜得多, 對于加載bean的功能,在Spring中的調(diào)用方式為:
?
MyBean?bean=(MyBean)?bf.getBean("myBean")
?
這句代碼實(shí)現(xiàn)了什么樣的功能呢?我們可以先快速體驗(yàn)一下Spring中代碼是如何實(shí)現(xiàn)的。
?
1 public Object getBean(String name) throws BeansException { 2 return doGetBean(name, null, null, false); 3 }?
1 @SuppressWarnings("unchecked") 2 protected <T> T doGetBean(final String name, final Class<T> requiredType, 3 final Object[] args, boolean typeCheckOnly) throws BeansException { 4 // 提取對應(yīng)的beanName 5 final String beanName = transformedBeanName(name); 6 Object bean; 7 8 // Eagerly check singleton cache for manually registered singletons. 9 /* 10 * 檢查緩存中或者實(shí)例工廠中是否有對應(yīng)的實(shí)例 為什么首先會(huì)使用這段代碼呢, 因?yàn)樵趧?chuàng)建單例bean的時(shí)候會(huì)存在依賴注入的情況,而在創(chuàng)建依賴的時(shí)候?yàn)榱吮苊庋h(huán)依賴, 11 * Spring創(chuàng)建bean的原則是不等bean創(chuàng)建完成就會(huì)將創(chuàng)建bean的ObjectFactory提早曝光 12 * 也就是將ObjectFactory加入到緩存中,一旦下個(gè)bean創(chuàng)建時(shí)候需要依賴上個(gè)bean則直接使用Object Factory 13 */ 14 // 直接嘗試從緩存獲取或者singletonFactories中的ObjectFactory中獲取 15 Object sharedInstance = getSingleton(beanName); 16 if (sharedInstance != null && args == null) { 17 if (logger.isDebugEnabled()) { 18 if (isSingletonCurrentlyInCreation(beanName)) { 19 logger.debug("Returning eagerly cached instance of singleton bean '" 20 + beanName 21 + "' that is not fully initialized yet - a consequence of a circular reference"); 22 } 23 else { 24 logger.debug("Returning cached instance of singleton bean '" 25 + beanName + "'"); 26 } 27 } 28 // 返回對應(yīng)的實(shí)例,有時(shí)候存在諸如BeanFactory的情況并不是直接返回實(shí)例本身而是返回指定方法返回的實(shí)例 29 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); 30 } 31 32 else { 33 // Fail if we're already creating this bean instance: 34 // We're assumably within a circular reference. 35 // 只有在單例情況才會(huì)嘗試解決循環(huán)依賴,原型模式情況下,如果存在 36 // A中有B的屬性,B中有A的屬性,那么當(dāng)依賴注入的時(shí)候,就會(huì)產(chǎn)生當(dāng)A還未創(chuàng)建完的時(shí)候因?yàn)? 37 // 對于B的創(chuàng)建再次返回創(chuàng)建A,造成循環(huán)依賴,也就是下面的情況 38 if (isPrototypeCurrentlyInCreation(beanName)) { 39 throw new BeanCurrentlyInCreationException(beanName); 40 } 41 42 // Check if bean definition exists in this factory. 43 // 如果beanDefinitionMap中也就是在所有已經(jīng)加載的類中不包括beanName則嘗試從parentBeanFactory中檢測 44 BeanFactory parentBeanFactory = getParentBeanFactory(); 45 if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { 46 // Not found -> check parent. 47 String nameToLookup = originalBeanName(name); 48 // 遞歸到BeanFactory中尋找 49 if (args != null) { 50 // Delegation to parent with explicit args. 51 return (T) parentBeanFactory.getBean(nameToLookup, args); 52 } 53 else { 54 // No args -> delegate to standard getBean method. 55 return parentBeanFactory.getBean(nameToLookup, requiredType); 56 } 57 } 58 // 如果不是僅僅做類型檢查則是創(chuàng)建bean,這里要進(jìn)行記錄 59 if (!typeCheckOnly) { 60 markBeanAsCreated(beanName); 61 } 62 63 try { 64 // 將存儲(chǔ)XML配置文件的GernericBeanDefinition轉(zhuǎn)換為RootBeanDefinition,如果指定BeanName是子Bean的話同時(shí)會(huì)合并父類的相關(guān)屬性 65 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); 66 checkMergedBeanDefinition(mbd, beanName, args); 67 68 // Guarantee initialization of beans that the current bean depends on. 69 String[] dependsOn = mbd.getDependsOn(); 70 // 若存在依賴則需要遞歸實(shí)例化依賴的bean 71 if (dependsOn != null) { 72 for (String dependsOnBean : dependsOn) { 73 if (isDependent(beanName, dependsOnBean)) { 74 throw new BeanCreationException( 75 "Circular depends-on relationship between '" 76 + beanName + "' and '" + dependsOnBean + "'"); 77 } 78 // 緩存依賴調(diào)用 79 registerDependentBean(dependsOnBean, beanName); 80 getBean(dependsOnBean); 81 } 82 } 83 84 // 實(shí)例化依賴的bean后便可以實(shí)例化mbd本身了 85 // Create bean instance. 86 if (mbd.isSingleton()) {// singleton模式的創(chuàng)建 87 sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { 88 89 @Override 90 public Object getObject() throws BeansException { 91 try { 92 return createBean(beanName, mbd, args); 93 } 94 catch (BeansException ex) { 95 // Explicitly remove instance from singleton cache: It 96 // might have been put there 97 // eagerly by the creation process, to allow for circular 98 // reference resolution. 99 // Also remove any beans that received a temporary 100 // reference to the bean. 101 destroySingleton(beanName); 102 throw ex; 103 } 104 } 105 }); 106 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); 107 } 108 109 else if (mbd.isPrototype()) { // prototype模式的創(chuàng)建(new) 110 // It's a prototype -> create a new instance. 111 Object prototypeInstance = null; 112 try { 113 beforePrototypeCreation(beanName); 114 prototypeInstance = createBean(beanName, mbd, args); 115 } 116 finally { 117 afterPrototypeCreation(beanName); 118 } 119 bean = getObjectForBeanInstance(prototypeInstance, name, beanName, 120 mbd); 121 } 122 123 else {// 指定的scope上實(shí)例化bean 124 String scopeName = mbd.getScope(); 125 final Scope scope = this.scopes.get(scopeName); 126 if (scope == null) { 127 throw new IllegalStateException("No Scope registered for scope '" 128 + scopeName + "'"); 129 } 130 try { 131 Object scopedInstance = scope.get(beanName, 132 new ObjectFactory<Object>() { 133 134 @Override 135 public Object getObject() throws BeansException { 136 beforePrototypeCreation(beanName); 137 try { 138 return createBean(beanName, mbd, args); 139 } 140 finally { 141 afterPrototypeCreation(beanName); 142 } 143 } 144 }); 145 bean = getObjectForBeanInstance(scopedInstance, name, beanName, 146 mbd); 147 } 148 catch (IllegalStateException ex) { 149 throw new BeanCreationException( 150 beanName, 151 "Scope '" 152 + scopeName 153 + "' is not active for the current thread; " 154 + "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", 155 ex); 156 } 157 } 158 } 159 catch (BeansException ex) { 160 cleanupAfterBeanCreationFailure(beanName); 161 throw ex; 162 } 163 } 164 165 // Check if required type matches the type of the actual bean instance. 166 // 檢查需要的類型是否符合bean的實(shí)際類型 167 if (requiredType != null && bean != null 168 && !requiredType.isAssignableFrom(bean.getClass())) { 169 try { 170 return getTypeConverter().convertIfNecessary(bean, requiredType); 171 } 172 catch (TypeMismatchException ex) { 173 if (logger.isDebugEnabled()) { 174 logger.debug( 175 "Failed to convert bean '" + name + "' to required type [" 176 + ClassUtils.getQualifiedName(requiredType) + "]", ex); 177 } 178 throw new BeanNotOfRequiredTypeException(name, requiredType, 179 bean.getClass()); 180 } 181 } 182 return (T) bean; 183 }?
?
僅從代碼量上就能看出來bean的加載經(jīng)歷了一個(gè)相當(dāng)復(fù)雜的過程,其中涉及各種各樣的考慮。相信讀者細(xì)心閱讀上面的代碼,并參照部分代碼注釋,是可以粗略地了解整個(gè)Spring加載bean的過程。對于加載過程中所涉及的步驟大致如下。
(1)轉(zhuǎn)換對應(yīng)beanName。
或許很多人不理解轉(zhuǎn)換對應(yīng)beanName是什么意思,傳入的參數(shù)name不就是beanName嗎?其實(shí)不是,這里傳入的參數(shù)可能是別名,也可能是FactoryBean,所以需要進(jìn)行一系列的解析,這些解析內(nèi)容包括如下內(nèi)容。
??去除FactoryBean的修飾符,也就是如果name="&aa",那么會(huì)首先去除&而使name="aa"。
??取指定alias所表示的最終beanName,例如別名A指向名稱為B的bean則返回B;若別名A指向別名B,別名B又指向名稱為C的bean則返回C。
?
(2)嘗試從緩存中加載單例。
?
單例在Spring的同一個(gè)容器內(nèi)只會(huì)被創(chuàng)建一次,后續(xù)再獲取bean,就直接從單例緩存中獲取了。當(dāng)然這里也只是嘗試加載,首先嘗試從緩存中加載,如果加載不成功則再次嘗試從singletonFactories中加載。因?yàn)樵趧?chuàng)建單例bean的時(shí)候會(huì)存在依賴注入的情況,而在創(chuàng)建依賴的時(shí)候?yàn)榱吮苊庋h(huán)依賴,在Spring中創(chuàng)建bean的原則是不等bean創(chuàng)建完成就會(huì)將創(chuàng)建bean的ObjectFactory提早曝光加入到緩存中,一旦下一個(gè)bean創(chuàng)建時(shí)候需要依賴上一個(gè)bean則直接使用ObjectFactory(后面章節(jié)會(huì)對循環(huán)依賴重點(diǎn)講解)。
?
(3)bean的實(shí)例化。
?
如果從緩存中得到了bean的原始狀態(tài),則需要對bean進(jìn)行實(shí)例化。這里有必要強(qiáng)調(diào)一下,緩存中記錄的只是最原始的bean狀態(tài),并不一定是我們最終想要的bean。舉個(gè)例子,假如我們需要對工廠bean進(jìn)行處理,那么這里得到的其實(shí)是工廠bean的初始狀態(tài),但是我們真正需要的是工廠bean中定義的factory-method方法中返回的bean,而getObjectForBeanInstance就是完成這個(gè)工作的,后續(xù)會(huì)詳細(xì)講解。
?
(4)原型模式的依賴檢查。
?
只有在單例情況下才會(huì)嘗試解決循環(huán)依賴,如果存在A中有B的屬性,B中有A的屬性,那么當(dāng)依賴注入的時(shí)候,就會(huì)產(chǎn)生當(dāng)A還未創(chuàng)建完的時(shí)候因?yàn)閷τ?/span>B的創(chuàng)建再次返回創(chuàng)建A,造成循環(huán)依賴,也就是情況:isPrototypeCurrentlyInCreation(beanName)判斷true。
?
(5)檢測parentBeanFactory。
?
從代碼上看,如果緩存沒有數(shù)據(jù)的話直接轉(zhuǎn)到父類工廠上去加載了,這是為什么呢?
?
可能讀者忽略了一個(gè)很重要的判斷條件:parentBeanFactory?!=?null?&&?!containsBean?Definition?(beanName),parentBeanFactory?!=?null。parentBeanFactory如果為空,則其他一切都是浮云,這個(gè)沒什么說的,但是!containsBeanDefinition(beanName)就比較重要了,它是在檢測如果當(dāng)前加載的XML配置文件中不包含beanName所對應(yīng)的配置,就只能到parentBeanFactory去嘗試下了,然后再去遞歸的調(diào)用getBean方法。
?
(6)將存儲(chǔ)XML配置文件的GernericBeanDefinition轉(zhuǎn)換為RootBeanDefinition。
?
因?yàn)閺?span style="font-family:'Times New Roman';">XML配置文件中讀取到的Bean信息是存儲(chǔ)在GernericBeanDefinition中的,但是所有的Bean后續(xù)處理都是針對于RootBeanDefinition的,所以這里需要進(jìn)行一個(gè)轉(zhuǎn)換,轉(zhuǎn)換的同時(shí)如果父類bean不為空的話,則會(huì)一并合并父類的屬性。
?
(7)尋找依賴。
?
因?yàn)?span style="font-family:'Times New Roman';">bean的初始化過程中很可能會(huì)用到某些屬性,而某些屬性很可能是動(dòng)態(tài)配置的,并且配置成依賴于其他的bean,那么這個(gè)時(shí)候就有必要先加載依賴的bean,所以,在Spring的加載順尋中,在初始化某一個(gè)bean的時(shí)候首先會(huì)初始化這個(gè)bean所對應(yīng)的依賴。
?
(8)針對不同的scope進(jìn)行bean的創(chuàng)建。
?
我們都知道,在Spring中存在著不同的scope,其中默認(rèn)的是singleton,但是還有些其他的配置諸如prototype、request之類的。在這個(gè)步驟中,Spring會(huì)根據(jù)不同的配置進(jìn)行不同的初始化策略。
?
(9)類型轉(zhuǎn)換。
?
程序到這里返回bean后已經(jīng)基本結(jié)束了,通常對該方法的調(diào)用參數(shù)requiredType是為空的,但是可能會(huì)存在這樣的情況,返回的bean其實(shí)是個(gè)String,但是requiredType卻傳入Integer類型,那么這時(shí)候本步驟就會(huì)起作用了,它的功能是將返回的bean轉(zhuǎn)換為requiredType所指定的類型。當(dāng)然,String轉(zhuǎn)換為Integer是最簡單的一種轉(zhuǎn)換,在Spring中提供了各種各樣的轉(zhuǎn)換器,用戶也可以自己擴(kuò)展轉(zhuǎn)換器來滿足需求。
?
?
經(jīng)過上面的步驟后bean的加載就結(jié)束了,這個(gè)時(shí)候就可以返回我們所需要的bean了,圖5-1直觀地反映了整個(gè)過程。其中最重要的就是步驟(8),針對不同的scope進(jìn)行bean的創(chuàng)建,你會(huì)看到各種常用的Spring特性在這里的實(shí)現(xiàn)。
?
在細(xì)化分析各個(gè)步驟提供的功能前,我們有必要先了解下FactoryBean的用法。
?
?
?
?
?
?
圖5-1??bean的獲取過程
?
?
?
?
?
?
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/mjorcen/p/3582956.html
總結(jié)
以上是生活随笔為你收集整理的5.0:Spring-bean的加载的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [spring]Attribute sc
- 下一篇: JavaScript高级程序设计58.p