不一样 使用别名 数据字段和bean_【修炼内功】[spring-framework] [3] Bean是如何创建又是如何销毁的?...
書接上文,在 [spring-framework] [2] BeanDefinitionReader 一文中簡(jiǎn)單介紹了XMLBeanFactory解析xml配置、并注冊(cè)BeanDefinition的邏輯,本文就bean的實(shí)例化過程及銷毀做簡(jiǎn)要分析
先放一張大圖(點(diǎn)擊圖片放大查看,右鍵或長(zhǎng)按保存后更清晰),展示完整的bean創(chuàng)建過程及銷毀過程,如果對(duì)spring原理有一些理解可將此圖作為開發(fā)過程中的參考,如果對(duì)spring原理還有一些模糊可繼續(xù)向下閱讀(長(zhǎng)文預(yù)警!)
BeanDefinition
通過前文簡(jiǎn)單了解到,Spring在初始化過程中并不是直接實(shí)例化bean,而是先收集所有bean的元數(shù)據(jù)信息并注冊(cè),bean的元數(shù)據(jù)描述為接口BeanDefinition,該接口定義了你能想到的一切有關(guān)bean的屬性信息
BeanDefinition衍生出一系列實(shí)現(xiàn)類
- AbstractBeanDefinition
如同其他Spring類,大部分BeanDefinition接口的邏輯都由該抽象類實(shí)現(xiàn) - GenericBeanDefinitionGenericBeanDefinition是一站式、用戶可見的bean definition,如何理解“用戶可見”?
可見的bean definition意味著可以在該bean definition上定義post-processor來對(duì)bean進(jìn)行操作 - RootBeanDefinition
當(dāng)bean definition存在父子關(guān)系的時(shí)候,RootBeanDefinition用來承載父元數(shù)據(jù)的角色(也可獨(dú)立存在),同時(shí)它也作為一個(gè)可合并的bean definition使用,在Spring初始化階段,所有的bean definition均會(huì)被(向父級(jí))合并為RootBeanDefinition,子bean definition(GenericBeanDefinition/ChildBeanDefinition)中的定義會(huì)覆蓋其父bean definition(由parentName指定)的定義 - ChildBeanDefinition
當(dāng)bean definition存在父子關(guān)系的時(shí)候,ChildBeanDefinition用來承載子元數(shù)據(jù)的角色(也可獨(dú)立存在),在Spring推出GenericBeanDefinition后,其完全可以被GenericBeanDefinition替代,目前使用場(chǎng)景已經(jīng)非常少 - AnnotatedBeanDefinition
如其名,主要用來定義注解場(chǎng)景的bean definition- ScannedGenericBeanDefinition
主要用來定義@Component、@Service等bean definition,其AnnotationMetadata metadata屬性用來存儲(chǔ)該bean的類注解信息 - AnnotatedGenericBeanDefinition
與ScannedGenericBeanDefinition不同的是,其主要用來定義@Configuration等配置類中@Bean的bean definition,其AnnotationMetadata metadata屬性與ScannedGenericBeanDefinition相同,MethodMetadata factoryMethodMetadata屬性用來存儲(chǔ)@Bean描述的方法信息
- ScannedGenericBeanDefinition
BeanDefinitionHolder只是簡(jiǎn)單捆綁了BeanDefinition、bean-name、bean-alias,用于注冊(cè)BeanDefinition及別名alias
BeanRegistry
在一般工程中,bean的定義分散在各種地方(尤其使用注解之后),Spring并不能在解析出每一個(gè)bean的元數(shù)據(jù)信息后立即對(duì)該bean做實(shí)例化動(dòng)作,對(duì)于依賴的分析與注入、類(方法)的代理、功能上的擴(kuò)展等,必須等所有的bean元數(shù)據(jù)全部解析完成之后才能進(jìn)行
在bean元數(shù)據(jù)解析完成之后、bean實(shí)例化之前,對(duì)bean的元數(shù)據(jù)信息有一個(gè)暫存的過程,這個(gè)過程便是bean的注冊(cè)
bean的注冊(cè)邏輯分兩步,一為BeanDefinition的注冊(cè),一為別名的注冊(cè)
- BeanDefinition注冊(cè)的定義在BeanDefinitionRegistry#registerBeanDefinition,其實(shí)現(xiàn)使用一個(gè)Map來保存bean-name和BeanDefinition的關(guān)系
- 別名的注冊(cè)定義在AliasRegistry#registerAlias,其實(shí)現(xiàn)同樣使用一個(gè)Map來保存別名alias-name和bean-name(或另一個(gè)別名alias-name)的關(guān)系
在完成bean的元數(shù)據(jù)注冊(cè)之后,便是根據(jù)詳盡的元數(shù)據(jù)信息進(jìn)行實(shí)例化了
BeanFactory
bean的實(shí)例化過程比較復(fù)雜(Spring考慮到了各種場(chǎng)景),附上BeanRegistry&BeanFactory相關(guān)的類依賴圖
初看此圖,請(qǐng)不要失去信心和耐心,圖中各類的作用會(huì)一一講解(見 #附錄#相關(guān)類說明),這里先介紹幾個(gè)核心的接口
- AliasRegistry
bean別名注冊(cè)和管理 - BeanDefinitionRegistry
bean元數(shù)據(jù)注冊(cè)和管理 - SingletonBeanRegistry
單例bean注冊(cè)和管理 - BeanFactory
bean工廠,提供各種bean的獲取及判斷方法
大致瀏覽上圖(類依賴圖)不難發(fā)現(xiàn),核心實(shí)現(xiàn)落在了DefaultListableBeanFactory,我們以此類作為切入點(diǎn)分析bean實(shí)例化過程
bean的實(shí)例化過程發(fā)生在getBean調(diào)用階段(對(duì)于singleton則發(fā)生在首次調(diào)用階段),getBean的實(shí)現(xiàn)方法眾多,我們追根溯源,找到最通用的方法AbstractBeanFactory#doGetBean
// org.springframework.beans.factory.support.AbstractBeanFactory protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {// 1. 獲取真正的beanNamefinal String beanName = transformedBeanName(name);Object bean;// 2. 嘗試獲取(提前曝光的)singleton bean實(shí)例(為了解決循環(huán)依賴)Object sharedInstance = getSingleton(beanName);// 3. 如果存在if (sharedInstance != null && args == null) { ... }// 4. 如果不存在else { ... }// 5. 嘗試類型轉(zhuǎn)換if (requiredType != null && !requiredType.isInstance(bean)) { ... }return (T) bean; }bean的實(shí)例化過程雖然復(fù)雜,但大體邏輯非常清楚
接下,就以上五個(gè)子流程(藍(lán)色部分)一一展開
在實(shí)例化bean的過程當(dāng)中,Spring會(huì)使用大量的中間態(tài)來判斷、處理各種場(chǎng)景和情況,此處先行列出Spring所使用的一些關(guān)鍵的中間態(tài)(各中間態(tài)的作用會(huì)在下文介紹,見 #附錄#中間態(tài)說明),以便在下文中更好地理解bean實(shí)例化過程中對(duì)各種情況的判斷和處理邏輯bean name轉(zhuǎn)換
在使用bean-name獲取bean的時(shí)候,除了可以使用原始bean-name之外,還可以使用alias別名等,bean-name的轉(zhuǎn)換則是將傳入的‘bean-name’一層層轉(zhuǎn)為最原始的bean-name
Return the bean name, stripping out the factory dereference prefix if necessary, and resolving aliases to canonical names.protected String transformedBeanName(String name) {return canonicalName(BeanFactoryUtils.transformedBeanName(name)); }函數(shù)canonicalName的作用則是利用別名注冊(cè)aliasMap,將別名alias轉(zhuǎn)為原始bean-name
函數(shù)transformedBeanName比較特殊,其是將FactoryBean的bean-name前綴 '&' 去除(BeanFactory#FACTORY_BEAN_PREFIX 下文會(huì)介紹)
嘗試獲取單例
拿到原始的bean-name之后,便可以實(shí)例化bean或者直接獲取已經(jīng)實(shí)例化的singleton-bean,此處為什么叫‘嘗試’獲取呢?
在獲取singleton-bean的時(shí)候一般存在三種情況:1. 還未實(shí)例化(或者不是單例);2. 已經(jīng)實(shí)例化;3. 正在實(shí)例化;
- 對(duì)于 “1. 還未實(shí)例化” ,返回null即可,后續(xù)進(jìn)行實(shí)例化動(dòng)作
- 對(duì)于“2. 已經(jīng)實(shí)例化”,直接返回實(shí)例化的singleton-bean
- 對(duì)于“3. 正在實(shí)例化”,則較難理解
Spring中對(duì)于singleton-bean,有一個(gè)sharedInstance的概念,在調(diào)用getSingleton函數(shù)時(shí),返回的不一定是完全實(shí)例化的singleton-bean,有可能是一個(gè)中間狀態(tài)(創(chuàng)建完成,但未進(jìn)行屬性依賴注入及其他后處理邏輯),這種中間狀態(tài)會(huì)通過getSingleton函數(shù)提前曝光出來,目的是為了解決循環(huán)依賴(下文會(huì)詳細(xì)介紹循環(huán)依賴)
在實(shí)例化beanA的過程中,需要依賴beanB和beanC,如果beanC同時(shí)又依賴beanA,則需要beanA在實(shí)例化完成之前提前曝光出來,以免造成beanA等待beanC實(shí)例化完成,beanC等待beanA實(shí)例化完成,類似一種死鎖的狀態(tài)在繼續(xù)進(jìn)行之前,有必要簡(jiǎn)單介紹幾個(gè)中間態(tài)(詳見 #附錄#中間態(tài)說明)
- singletonObjects
緩存已經(jīng)實(shí)例化完成的singleton-bean - earlySingletonObjects
緩存正在實(shí)例化的、提前曝光的singleton-bean,用于處理循環(huán)依賴 - singletonFactories
緩存用于生成earlySingletonObject的 ObjectFactory
介紹了上述之后,再來描述getSingleton的邏輯就會(huì)比較清楚
不用糾結(jié)上述中間態(tài)的值是何時(shí)被設(shè)置進(jìn)去的,下文會(huì)逐步提及FactoryBean處理(sharedInstance存在的邏輯)
上述 sharedInstance 一定是我們需要的bean實(shí)例么?未必!
定義bean的時(shí)候可以通過實(shí)現(xiàn)FactoryBean接口來定制bean實(shí)例化的邏輯,以此增加更多的靈活性及可能性
How to use the Spring FactoryBean? | Baeldung?www.baeldung.com@Bean(initMethod = "init", destroyMethod = "destroy") public FactoryBean myBean() {return new FactoryBean<MyBean>() {/*** 定制bean初始化邏輯*/@Overridepublic MyBean getObject() throws Exception {MyBean myBean = new MyBean();// ... 定制化的邏輯return myBean;}/*** 真正的bean類型*/@Overridepublic Class<?> getObjectType() {return MyBean.class;}@Overridepublic boolean isSingleton() {return true;}} }通過注冊(cè)FactoryBean類型的bean,實(shí)例化后的原始實(shí)例類型同樣為FactoryBean,但我們需要的是通過FactoryBean#getObject方法得到的實(shí)例,這便需要針對(duì)FactoryBean做一些處理,這也是接下來要講解的函數(shù)AbstractBeanFactory#getObjectForBeanInstance
Get the object for the given bean instance, either the bean instance itself or its created object in case of a FactoryBean.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.
該函數(shù)要實(shí)現(xiàn)的邏輯比較簡(jiǎn)單,如果sharedInstance是 FactoryBean,則使用getObject方法創(chuàng)建真正的實(shí)例
getObjectForBeanInstance是一個(gè)通用函數(shù),并不只針對(duì)通過getSingleton得到的sharedInstance,任何通過緩存或者創(chuàng)建得到的 rawInstance,都需要經(jīng)過getObjectForBeanInstance處理,拿到真正需要的 beanInstance簡(jiǎn)單介紹getObjectForBeanInstance函數(shù)的入?yún)?/p>/*** @param beanInstance sharedInstance / rawInstance,可能為FactoryBean* @param name 傳入的未做轉(zhuǎn)換的 bean name* @param beanName 對(duì)name做過轉(zhuǎn)換后的原始 canonical bean name* @param mbd 合并后的RootBeanDefinition,下文會(huì)介紹*/ protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd)
getObjectForBeanInstance函數(shù)的處理邏輯
上圖中有一個(gè)邏輯判斷,如果入?yún)ame以'&' (BeanFactory#FACTORY_BEAN_PREFIX)開頭則直接返回(BeanFactory)
這里兼容了一種情況,如果需要獲取/注入FactoryBean而不是getObject生成的實(shí)例,則需要在bean-name/alias-name前加入'&'
/*** 注入FactoryBean#getObject生成的實(shí)例*/ @Autowired private MyBean myBean;/*** 直接注入FactoryBean*/ @Resource(name = "&myBean") private FactoryBean<MyBean> myFactoryBean;對(duì)于singleton,FactoryBean#getObject的結(jié)果會(huì)被緩存到factoryBeanObjectCache,對(duì)于緩存中不存在或者不是singleton的情況,會(huì)通過FactoryBean#getObject生成(上圖中藍(lán)色未展開的邏輯)
Spring并非簡(jiǎn)單的調(diào)用FactoryBean#getObject,而是分為兩部分處理
bean instance生成
上圖中doGetObjectFromFactoryBean,主要對(duì)getObject方法進(jìn)行了包裝,判斷是否需要在SecurityManager框架內(nèi)執(zhí)行以及對(duì)null結(jié)果進(jìn)行封裝(NullBean)
bean instance后處理
上圖中postProcessObjectFromFactoryBean,主要對(duì)生成的bean instance做一些后處理(可以跟蹤到AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization),
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory @Override public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;for (BeanPostProcessor processor : getBeanPostProcessors()) {// 拿到所有注冊(cè)的BeanPostProcessor,執(zhí)行后處理動(dòng)作Object current = processor.postProcessAfterInitialization(result, beanName);if (current == null) {return result;}result = current;}return result; }postProcessAfterInitialization函數(shù)可以對(duì)現(xiàn)有bean instance做進(jìn)一步的處理,甚至可以返回新的bean instance,這就為bean的增強(qiáng)提供了一個(gè)非常方便的擴(kuò)展方式(可以思考一下,AOP的代理類是如何生成的)
加載bean實(shí)例(sharedInstance不存在的邏輯)
以上,討論了bean instance存在于緩存中的情況,如果緩存中不存則需要進(jìn)行bean的加載
簡(jiǎn)單來講,bean的加載/創(chuàng)建分為三大部分
這里類似類繼承,子BeanDefinition屬性會(huì)覆蓋父BeanDefinition
對(duì)于有依賴的情況,優(yōu)先遞歸加載依賴的bean
BeanDefinition合并(RootBeanDefinition)
將BeanDefinition轉(zhuǎn)為RootBeanDefinition,如果存在父子關(guān)系,則進(jìn)行合并
這里不再贅述,可以參考 AbstractBeanFactory#getMergedLocalBeanDefinition
加載depends-on beans
String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) {// 遍歷所有的依賴for (String dep : dependsOn) {// 檢測(cè)循環(huán)依賴if (isDependent(beanName, dep)) { /* throw exception */ }// 注冊(cè)依賴關(guān)系registerDependentBean(dep, beanName);// 遞歸getBean,加載依賴beantry { getBean(dep); }catch (NoSuchBeanDefinitionException ex) { /* throw exception */ }} }邏輯很簡(jiǎn)單,但這里涉及到兩個(gè)中間態(tài)dependentBeanMap、dependenciesForBeanMap
- dependentBeanMap
存儲(chǔ)哪些bean依賴了我(哪些bean里注入了我)
如果 beanB -> beanA, beanC -> beanA,key為beanA,value為[beanB, beanC] - dependenciesForBeanMap
存儲(chǔ)我依賴了哪些bean(我注入了哪些bean)
如果 beanA -> beanB, beanA -> beanC,key為beanA,value為[beanB, beanC]
理解兩者的存儲(chǔ)關(guān)系,有助于在閱讀源碼的過程中理解bean的加載和銷毀順序
加載singleton bean實(shí)例
if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {// singletonFactory - ObjectFactorytry { return createBean(beanName, mbd, args); }catch (BeansException ex) { destroySingleton(beanName); throw ex; }});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }這里涉及兩個(gè)比較核心的函數(shù)createBean、getObjectForBeanInstance
- createBean
根據(jù)BeanDefinition的內(nèi)容,創(chuàng)建/初始化bean instance - getObjectForBeanInstance
上文已經(jīng)介紹過,主要處理FactoryBean,將FactoryBean轉(zhuǎn)為真正需要的bean instance
createBean被包裝在lambda(singletonFactory)中作為getSingleton的參數(shù),我們來看getSingleton的實(shí)現(xiàn)邏輯
所以,關(guān)鍵的邏輯在createBean函數(shù)中,bean的創(chuàng)建邏輯較為復(fù)雜,我們放到后面介紹
加載prototype bean實(shí)例
else if (mbd.isPrototype()) {Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally { afterPrototypeCreation(beanName); }bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); }prototype bean的創(chuàng)建與singleton bean類似,只是不會(huì)緩存創(chuàng)建完成的bean
加載其他scope bean實(shí)例
scope,即作用域,或者可以理解為生命周期
上文介紹了singleton-bean及prototype-bean的創(chuàng)建過程,嚴(yán)格意義上講以上兩種都是一種特殊的scope-bean,分別對(duì)應(yīng)ConfigurableBeanFactory#SCOPE_SINGLETON及ConfigurableBeanFactory#SCOPE_PROTOTYPE,前者作用域?yàn)檎麄€(gè)IOC容器,也可理解為單例,后者作用域?yàn)樗⑷氲腷ean,每次注入(每次觸發(fā)getBean)都會(huì)重新生成
Spring中還提供很多其他的scope,如WebApplicationContext#SCOPE_REQUEST或WebApplicationContext#SCOPE_SESSION,前者作用域?yàn)橐淮蝫eb request,后者作用域?yàn)橐粋€(gè)web session周期
自定義scope的bean實(shí)例創(chuàng)建過程與singleton bean的創(chuàng)建過程十分相似,需要實(shí)現(xiàn)Scope的get方法(org.springframework.beans.factory.config.Scope#get),
else {String scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);if (scope == null) { /* throw exception */ }try {Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);// createBean被封裝在Scope#get函數(shù)的lambda參數(shù)ObjectFactory中try { return createBean(beanName, mbd, args); }finally { afterPrototypeCreation(beanName); }});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);} catch (IllegalStateException ex) { /* throw exception */} }Scope接口除了get方法之外,還有一個(gè)remove方法,前者用于定義bean的初始化邏輯,后者用于定義bean的銷毀邏輯
public interface Scope {/*** Return the object with the given name from the underlying scope*/Object get(String name, ObjectFactory<?> objectFactory);/*** Remove the object with the given name from the underlying scope.*/Object remove(String name); }WebApplicationContext#SCOPE_SESSION對(duì)應(yīng)的Scope實(shí)現(xiàn)見org.springframework.web.context.request.SessionScope
WebApplicationContext#SCOPE_REQUEST對(duì)應(yīng)的Scope實(shí)現(xiàn)見org.springframework.web.context.request.RequestScope
以上兩種Scope實(shí)現(xiàn)都較為簡(jiǎn)單,前者將初始化的bean存儲(chǔ)在request attribute種,后者將初始化的bean存儲(chǔ)在http session中,具體細(xì)節(jié)請(qǐng)自行查閱源碼
Q: Spring中實(shí)現(xiàn)了哪些Scope?又是什么時(shí)候注冊(cè)的?Bean創(chuàng)建過程
AbstractAutowireCapableBeanFactory#createBean
了解bean創(chuàng)建的過程也是一個(gè)抽絲剝繭的過程,真正創(chuàng)建的過程封裝在AbstractAutowireCapableBeanFactory#doCreateBean中,而在此之前有一些準(zhǔn)備工作,整體流程如下圖
這一步驟用于鎖定bean class,在沒有顯示指定beanClass的情況下,使用className加載beanClass
2. 驗(yàn)證method overrides
在 [spring-framework] [2] BeanDefinitionReader 一文中有提到過lookup-method及replace-method,該步驟是為了確認(rèn)以上兩種配置中的method是否存在
3. 執(zhí)行InstantiationAwareBeanPostProcessor前處理器(postProcessBeforeInstantiation)
這里要特別注意的是,如果這個(gè)步驟中生成了“代理”bean instance,則會(huì)有一個(gè)短路操作,直接返回該bean instance而不再執(zhí)行doCreate,這是一個(gè)“細(xì)思極恐”的操作,但在一些特殊場(chǎng)景(尤其框架之中)提供了良好的擴(kuò)展機(jī)制
try {// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.Object bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {// 如果這里生成了代理的bean instance會(huì)直接返回return bean;} } cache (Throwable ex) { // throw exception }try {// 創(chuàng)建bean instanceObject beanInstance = doCreateBean(beanName, mbdToUse, args);// ... }Q: InstantiationAwareBeanPostProcessor的使用場(chǎng)景有哪些?Spring有哪些功能使用了InstantiationAwareBeanPostProcessor?它們是在什么時(shí)候注冊(cè)的?4. doCreateBean (AbstractAutowireCapableBeanFactory)
真正bean的創(chuàng)建及初始化過程在此處實(shí)現(xiàn),但Spring對(duì)bean創(chuàng)建及初始化邏輯的復(fù)雜程度完全超出了本篇文章之承載,這里只對(duì)一些關(guān)鍵的邏輯做梳理
創(chuàng)建bean實(shí)體
AbstractAutowireCapableBeanFactory#createBeanInstance
從上面的流程圖可以看出,創(chuàng)建bean實(shí)體不一定會(huì)使用到構(gòu)造函數(shù),有兩個(gè)特殊的方式
1. instance supplier
AbstractAutowireCapableBeanFactory#obtainFromSupplier
從Spring5開始,多了一種以函數(shù)式注冊(cè)bean的方式(參考https://www.baeldung.com/spring-5-functional-beans)
// 注冊(cè)MyService context.registerBean(MyService.class, () -> new MyService()); // 注冊(cè)MyService,并指定bean name context.registerBean("mySecondService", MyService.class, () -> {MyService myService = new MyService();// 其他的邏輯return myService; }); // 注冊(cè)MyService,指定bean name,并增強(qiáng)bean definition context.registerBean("myCallbackService", MyService.class, () -> {MyService myService = new MyService();// 其他的邏輯return myService; }), bd -> bd.setAutowireCandidate(false));或者
// 構(gòu)建BeanDefinition BeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(MyService.class, () -> {MyService myService = new MyService();// 其他的邏輯return myService; }); // 注冊(cè)BeanDefinition beanFactory.registerBeanDefinition("myService", bd);通過以上方式注冊(cè)的bean,Spring會(huì)調(diào)用該supplier生成bean實(shí)體
2. factory method
AbstractAutowireCapableBeanFactory#instantiateUsingFactoryMethod
ConstructorResolver#instantiateUsingFactoryMethod
在xml配置中還有一種不太常見的bean注冊(cè)方式
public class MyHome {// 靜態(tài)方法public static MyHome create() {return new MyHome();} }public class MyFactory {// 非靜態(tài)方法public MyHome create() {return new MyHome();} }<bean id="myFactory" class="com.manerfan.MyFactory"></bean><!-- 方式一 --> <!-- 使用指定類的靜態(tài)方法 --> <bean id="myHome1" class="com.manerfan.MyHome" factory-method="create"></bean> <!-- 方式二 --> <!-- 使用指定bean的非靜態(tài)方法 --> <bean id="myHome2" class="com.manerfan.MyHome" factory-method="create" factory-bean="myFactory"></bean>Spring會(huì)通過指定類的指定方法生成bean實(shí)體,其中有兩種方式,一種方式(僅指定factory-method)使用指定類的靜態(tài)方法生成,另一種方式(同時(shí)指定factory-method和factory-bean)使用指定bean的非靜態(tài)方法生成
同時(shí)factory-method中還允許傳入一些參數(shù),如果存在同名函數(shù),Spring會(huì)根據(jù)參數(shù)的個(gè)數(shù)及類型找到匹配的method
public class MyHome {private MyHouse house;private MyCar car;// setters }public class MyFactory {// 攜帶入?yún)ublic MyHome create(MyHouse house, MyCar car) {MyHome myHome = new MyHome();myHome.setHouse(house);myHome.setCar(car)return myHome;}// 同名函數(shù)public MyHome create(MyHouse house) {MyHome myHome = new MyHome();myHome.setHouse(house);myHome.setCar(defaultCar)return myHome;} }<bean id="myHome2" class="com.manerfan.MyHome" factory-method="create" factory-bean="myFactory"><!-- 這里使用的是構(gòu)造函數(shù)參數(shù)類型 --><constructor-arg name="house" ref="house"/><constructor-arg name="car" ref="car"/> </bean>這樣的代碼是不是讓你想到了@Configuration中的@Bean,@Bean所修飾方法如果存在參數(shù)的話,Spring會(huì)通過參數(shù)的類型及名稱自動(dòng)進(jìn)行依賴注入
@Configuration public class MyFactory {@Beanpublic MyHome create(MyHouse house, MyCar car) {MyHome myHome = new MyHome();myHome.setHouse(house);myHome.setCar(car)return myHome;} }我們可以大膽猜測(cè),@Configuration + @Bean的實(shí)現(xiàn)方式就是factory-bean + factory-method,在后文介紹Spring的注解體系時(shí)會(huì)揭曉
使用指定(類)bean的(靜態(tài))方法創(chuàng)建bean實(shí)體的邏輯在ConstructorResolver#instantiate(String, RootBeanDefinition, Object, Method, args),而真正的邏輯在SimpleInstantiationStrategy#instantiate(RootBeanDefinition, String, BeanFactory, Object, Method, Object...),其核心的執(zhí)行邏輯非常簡(jiǎn)單,有了方法factoryMethod(factoryBean)及入?yún)rgs,便可以調(diào)用該方法創(chuàng)建bean實(shí)體
Object result = factoryMethod.invoke(factoryBean, args);factoryBean可以通過beanFactory.getBean獲取到(正是當(dāng)前在講的邏輯),factoryMethod可以通過反射獲取到,入?yún)rgs如何獲取?這便涉及到Spring中的一個(gè)重要概念 -- 依賴注入,如何準(zhǔn)確的找到依賴的實(shí)體并將其注入,便是接下來的重點(diǎn),這里涉及到一個(gè)非常重要的函數(shù)ConstructorResolver#resolvePreparedArguments,該函數(shù)的作用是將BeanDefinition中定義的入?yún)⑥D(zhuǎn)換為真是需要的參數(shù)(xml中定義的或者注解中定義的),在[spring-framework] [2] BeanDefinitionReader 一文中有過介紹,ref會(huì)被封裝為RuntimeBeanReference存儲(chǔ)、value會(huì)被封裝為TypedStringValue存儲(chǔ)等等,如何將這些封裝好的存儲(chǔ)類型轉(zhuǎn)為真正需要的函數(shù)參數(shù),便是ConstructorResolver#resolvePreparedArguments函數(shù)的作用
這里分成了三大分支
針對(duì)BeanMetadataElement,進(jìn)行值的轉(zhuǎn)換,其中又會(huì)包含特別細(xì)的分支,大致如下
- RuntimeBeanNameReference
AbstractBeanFactory#evaluateBeanDefinitionString
支持Spl表達(dá)式解析bean name - BeanDefinitionHolder 、BeanDefinition
BeanDefinitionValueResolver#resolveInnerBean
與createBean函數(shù)的邏輯類似,創(chuàng)建一個(gè)inner bean - DependencyDescriptor
AutowireCapableBeanFactory#resolveDependency
用來處理OptionalBean、LazyBean、AutowireCandidateBean等(詳見下文“注解注入”一節(jié)) - ManagedArray、ManagedList、ManagedSet、ManagedMap
BeanDefinitionValueResolver#resolveManagedArray
BeanDefinitionValueResolver#resolveManagedList
BeanDefinitionValueResolver#resolveManagedSet
BeanDefinitionValueResolver#resolveManagedMap
內(nèi)部遞歸使用resolveValueIfNecessary方法獲取bean并最終封裝成對(duì)應(yīng)的類型 - ManagedProperties
通過BeanDefinitionValueResolver#evaluate(Spel)計(jì)算value的值,最終封裝為Properties - TypedStringValue
通過BeanDefinitionValueResolver#evaluate(Spel)計(jì)算value的值
對(duì)于這部分內(nèi)容,Spring在接下來的發(fā)展中可能還會(huì)不斷地?cái)U(kuò)充
2. String
AbstractBeanFactory#evaluateBeanDefinitionString
與resolveValueIfNecessary中的RuntimeBeanNameReference一致,支持Spl表達(dá)式解析表達(dá)式
3. 其他
- InjectionPoint
使用ThreadLocal提供當(dāng)前bean被注入到的注入點(diǎn),可以參考
- 其他
與resolveValueIfNecessary中的DependencyDescriptor一致,用來處理OptionalBean、LazyBean等
在返回之前還有convertIfNecessary的方法調(diào)用,該函數(shù)是將上述解析得到的值轉(zhuǎn)換為函數(shù)參數(shù)真正的類型
為何要轉(zhuǎn)換?其實(shí)上述過程拿到的值并非真正需要的值,如
public class MyComponent {private Resource initSql; }<bean id="myComponent" class="com.manerfan.MyComponent"><property name="initSql" value="classpath:/init/sql/init.sql"></property> </bean>或者
public class MyComponent {@Value("${init.sql}")// or @Value("classpath:/init/sql/init.sql")private Resource initSql; } init.sql=classpath:/init/sql/init.sql不論哪種形式,在convertIfNecessary之前解析到的值都是字符串 "classpath:/init/sql/init.sql",convertIfNecessary的作用便是將上述解析得到的值轉(zhuǎn)換為函數(shù)參數(shù)真正的類型Resource
convert的邏輯在TypeConverterDelegate#convertIfNecessary,其內(nèi)部基本的邏輯為
將url轉(zhuǎn)換為Resource的PropertyEditor對(duì)應(yīng)為 org.springframework.core.io.ResourceEditor,正是使用ResourceEditor將 字符串"classpath:/init/sql/init.sql”轉(zhuǎn)為對(duì)應(yīng)的Resource
Q: Spring默認(rèn)的PropertyEditor有哪些?又是什么時(shí)候注冊(cè)的?3. 有參構(gòu)造函數(shù)
AbstractAutowireCapableBeanFactory#autowireConstructor
ConstructorResolver#autowireConstructor
使用有參構(gòu)造函數(shù)創(chuàng)建bean實(shí)例的一個(gè)點(diǎn)在于尋找與參數(shù)相對(duì)應(yīng)的構(gòu)造函數(shù)(可能定義了多個(gè)構(gòu)造函數(shù)),而對(duì)于參數(shù)的解析和轉(zhuǎn)換(參數(shù)的依賴注入)則與使用factory method一樣,調(diào)用ConstructorResolver#resolvePreparedArguments函數(shù)進(jìn)行處理,這里不再重復(fù)描述
在拿到真實(shí)的入?yún)⒓皩?duì)應(yīng)的構(gòu)造函數(shù)后,下一步便是使用構(gòu)造函數(shù)來創(chuàng)建bean實(shí)例,但事情貌似也并沒有那么簡(jiǎn)單
實(shí)例化的過程在ConstructorResolver#instantiate,內(nèi)部并沒有統(tǒng)一利用反射技術(shù)直接使用構(gòu)造函數(shù)創(chuàng)建,而是分為兩種情況
一種,沒有設(shè)置override-method時(shí),直接使用構(gòu)造函數(shù)創(chuàng)建
一種,在設(shè)置了override-method時(shí),使用cglib技術(shù)構(gòu)造代理類,并代理override方法
以上,Spring默認(rèn)的實(shí)例化策略為CglibSubclassingInstantiationStrategy
4. 無參構(gòu)造函數(shù)
AbstractAutowireCapableBeanFactory#instantiateBean
無參構(gòu)造函數(shù)創(chuàng)建bean實(shí)例的過程與有參構(gòu)造函數(shù)創(chuàng)建過程完全一致,只是少了參數(shù)的依賴注入,使用默認(rèn)無參構(gòu)造函數(shù)進(jìn)行實(shí)例化
BeanDefinition后處理
AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors
在屬性注入之前提供一次機(jī)會(huì)來對(duì)BeanDefinition進(jìn)行處理,內(nèi)部執(zhí)行所有注冊(cè)MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法
在閱讀源碼時(shí)注意到一個(gè)MergedBeanDefinitionPostProcessor的實(shí)現(xiàn)類 AutowiredAnnotationBeanPostProcessor,深入到實(shí)現(xiàn)內(nèi)部AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata,其實(shí)現(xiàn)了兩個(gè)注解類的解析 @Value 及 @Autowired ,找到注解修飾的Filed或者M(jìn)ethod并緩存,具體的邏輯會(huì)在屬性注入一節(jié)中詳細(xì)介紹
Q: Spring注冊(cè)了哪些MergedBeanDefinitionPostProcessor?它們都是做什么用的?又是什么時(shí)候注冊(cè)的?提前暴露實(shí)體
DefaultSingletonBeanRegistry#addSingletonFactory -> AbstractAutowireCapableBeanFactory#getEarlyBeanReference
還記得上文介紹的“嘗試獲取單例”(AbstractBeanFactory.getSingleton)么?為了解決循環(huán)依賴會(huì)將singleton-bean提前暴露出來,暴露的邏輯會(huì)封裝為ObjectFactory(AbstractAutowireCapableBeanFactory#getEarlyBeanReference實(shí)現(xiàn))緩存在DefaultSingletonBeanRegistry.singletonFactories中,在getBean的邏輯getSingleton中會(huì)執(zhí)行ObjectFactory的邏輯將singleton提前暴露
此時(shí)暴露的singleton-bean僅完成了bean的實(shí)例化,屬性注入、初始化等邏輯均暫未執(zhí)行屬性注入
AbstractAutowireCapableBeanFactory#populateBean
在“創(chuàng)建bean實(shí)體”小節(jié)中介紹了factory method方式及有參構(gòu)造函數(shù)方式的參數(shù)注入邏輯,除此之外還有一種注入便是屬性注入
流程圖中兩次出現(xiàn)了InstantiationAwareBeanPostProcessor,還記得在“Bean創(chuàng)建過程”小結(jié)中介紹的InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation么?如果該步驟生成了“代理”bean instance,則會(huì)有一個(gè)短路操作,直接返回該bean instance而不再執(zhí)行后續(xù)的doCreate
InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation同樣是一個(gè)短路操作,如果有任意一個(gè)InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法返回false,則會(huì)跳出屬性注入的邏輯,官方對(duì)此的解釋如下
Give any InstantiationAwareBeanPostProcessors the opportunity to modify the state of the bean before properties are set. This can be used, for example, to support styles of field injection.autowireByName及autowireByType方法作為“候補(bǔ)”補(bǔ)充BeanDefinition的propertyValues
Fill in any missing property values with references to other beans.PropertyValue中記錄了需要注入的屬性信息及需要注入的屬性值,那BeanDefinition的propertyValues都來自哪里?xml中的bean配置、自定義的BeanDefinition等
public class MyService {/*** string*/private String name;/*** resource*/private Resource res;/*** bean ref*/private MyComponent myComponent; }xml中定義PropertyValue
<bean id="myMyService" class="com.manerfan.MyService"><property name="name" value="SpringDemoApp"></property><property name="res" value="classpath:/init/init.sql"></property><property name="myComponent" ref="myComponent"></property> </bean>BeanDefinition中直接定義PropertyValue
// 構(gòu)建BeanDefinition BeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(MyService.class).addPropertyValue("name", "${spring.application.name}").addPropertyValue("res", "classpath:/init/init.sql").addPropertyReference("myComponent", "myComponent").getBeanDefinition(); // 注冊(cè)BeanDefinition beanFactory.registerBeanDefinition("myService", bd);所有的ProperValue均會(huì)在AbstractAutowireCapableBeanFactory#applyPropertyValues中進(jìn)行依賴的解析、轉(zhuǎn)換并設(shè)置到bean實(shí)例對(duì)應(yīng)的屬性中,詳細(xì)的邏輯下文介紹
注解注入
除此之外通過注解修飾的屬性(方法)是如何注入的?
public class MyService {/*** string*/@Value("${spring.application.name}")private String name;/*** resource*/@Value("classpath:/init/init.sql")private Resource res;/*** bean ref by parameter*/@Autowired@Qualifier("myComponent1")// or @Resource("myComponent1")// or @Injectprivate MyComponent myComponent1;private MyComponent myComponent2;/*** bean ref by setter method*/@Autowiredpublic void setMyComponet2(@Qualifier("myComponent1") MyComponet component) {this.myComponent2 = component} }各注解的使用可以參考 https://www.baeldung.com/spring-annotations-resource-inject-autowireWiring in Spring: @Autowired, @Resource and @Inject | Baeldung?www.baeldung.com在AbstractAutowireCapableBeanFactory#applyPropertyValues之前發(fā)現(xiàn)還有一個(gè)動(dòng)作InstantiationAwareBeanPostProcessor#postProcessProperties(是的InstantiationAwareBeanPostProcessor又出現(xiàn)了),在此有兩個(gè)實(shí)現(xiàn)引起了我的注意AutowiredAnnotationBeanPostProcessor#postProcessProperties及CommonAnnotationBeanPostProcessor#postProcessProperties,我們來對(duì)比兩個(gè)實(shí)現(xiàn)的內(nèi)部邏輯
// AutowiredAnnotationBeanPostProcessor#postProcessProperties public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs);} catch (Throwable ex) { /* handle exception */ }return pvs; } // CommonAnnotationBeanPostProcessor#postProcessProperties public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs);} catch (Throwable ex) { /* handle exception */ }return pvs; }從代碼及流程圖可以看出,兩種實(shí)現(xiàn)的差異僅在InjectionMetadata的查找邏輯,一個(gè)個(gè)來
AutowiredAnnotation
AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata的核心邏輯可以追蹤到AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata
CommonAnnotation
CommonAnnotationBeanPostProcessor#findResourceMetadata的核心邏輯可以追蹤到CommonAnnotationBeanPostProcessor.buildResourceMetadata
InjectionElement
以上,對(duì)于不同的注解不同的方式(屬性/方法),會(huì)被封裝為不同的InjectionElement,并最終將所有的InjectionElement封裝在InjectionMetadata中
在找到InjectionElement之后,下一步便是依賴的解析和注入了(InjectionMetadata#inject)
這里的邏輯無非就是遍歷內(nèi)部所有的InjectionElement并執(zhí)行InjectionElement.inject,上面已經(jīng)介紹,對(duì)于不同的注解不同的方式(屬性/方法),會(huì)被封裝為不同的InjectionElement,那不同的InjectionElement也會(huì)有不同的inject邏輯,至此我們大致可以理出一個(gè)注解注入的大框架
所以,歸根結(jié)底,注入的過程在AutowiredFieldElement、AutowiredMethodElement、ResourceElement、等InjectionElement內(nèi)部,在繼續(xù)進(jìn)行之前有必要了解一下DefaultListableBeanFactory#resolveDependency
還記得上文“創(chuàng)建bean實(shí)體”一節(jié)中介紹參數(shù)的注入時(shí)提到的AutowireCapableBeanFactory#resolveDependency么?該函數(shù)正是調(diào)用了DefaultListableBeanFactory#resolveDependency,上文并未詳細(xì)展開該函數(shù)的邏輯實(shí)現(xiàn),其除了處理OptionalBean、及LazyBean之外,我們比較關(guān)心的邏輯在DefaultListableBeanFactory#doResolveDependency
該函數(shù)處理了@Value、@Qualifier、@Primary、@Order等的邏輯
@Value的解析有兩個(gè)過程,1. StringValueResolver解析(${spring.sql.init} -> classpath:/init/init.sql);2. PropertyEditor轉(zhuǎn)換(classpath:/init/init.sql -> Resouce);
AutowiredFieldElement無非就是使用DefaultListableBeanFactory#doResolveDependency將依賴的bean解析到,并設(shè)置到對(duì)應(yīng)的屬性上
AutowiredMethodElement則是使用DefaultListableBeanFactory#doResolveDependency將參數(shù)對(duì)應(yīng)依賴的bean解析到,并執(zhí)行對(duì)應(yīng)的方法
Q: 我們是否可以自定義注解(InstantiationAwareBeanPostProcessor),來實(shí)現(xiàn)類似 @Value、@Autowired 的功能?屬性注入
AbstractAutowireCapableBeanFactory#applyPropertyValues
還記得在一開始提到的BeanDefinition中的propertyValues么?(xml中的bean配置、自定義的BeanDefinition,也有可能來自InstantiationAwareBeanPostProcessor#postProcessProperties),至此這一部分的屬性還未注入依賴
PropertyValue中記錄了需要注入的屬性,已經(jīng)依賴的類型(String、RuntimeBeanReference、等),根據(jù)不同的類型解析依賴的bean并設(shè)置到對(duì)應(yīng)的屬性上(此過程與DefaultListableBeanFactory#doResolveDependency極其相似,不再贅述)
初始化
AbstractAutowireCapableBeanFactory#initializeBean
以上,完成了bean實(shí)例的創(chuàng)建和屬性注入,之后還有一些初始化的方法,比如各種Aware的setXxx是如何調(diào)用的、@PostConstruct是怎么調(diào)用的?
Q: Aware類有很多,除了上圖中的三種之外,其他的Aware是什么時(shí)候調(diào)用的?Q: @PreDestroy是如何調(diào)用的?destroy-method是何時(shí)執(zhí)行的?
Q: AbstractAdvisingBeanPostProcessor都做了什么?是如何處理AOP代理的?
注冊(cè)Disposable
AbstractBeanFactory#registerDisposableBeanIfNecessary
至此,終于完成了bean實(shí)例的創(chuàng)建、屬性注入以及之后的初始化,此后便可以開始使用了
在使用Spring的過程中經(jīng)常還會(huì)碰到設(shè)置銷毀邏輯的情況,如數(shù)據(jù)庫(kù)連接池、線程池等等,在Spring銷毀bean的時(shí)候還需要做一些處理,類似于C++中的析構(gòu)
在bean的創(chuàng)建邏輯中,最后一個(gè)步驟則是注冊(cè)bean的銷毀邏輯(DisposableBean)
銷毀邏輯的注冊(cè)有幾個(gè)條件
滿足以上條件的bean會(huì)被封裝為DisposableBeanAdapter,并注冊(cè)在DefaultSingletonBeanRegistry.disposableBeans中(詳見附錄#中間態(tài)說明)
Q: 理解了bean的銷毀注冊(cè)邏輯,那bean的銷毀時(shí)何時(shí)觸發(fā)以及如何執(zhí)行的?嘗試類型轉(zhuǎn)換
以上,完成了bean的創(chuàng)建、屬性的注入、dispose邏輯的注冊(cè),但獲得的bean類型與實(shí)際需要的類型可能依然不相符,在最終交付bean之前(getBean)還需要進(jìn)行一次類型轉(zhuǎn)換,上文反復(fù)提到過PropertyEditor,此處不例外,使用的既是PropertyEditor進(jìn)行的類型轉(zhuǎn)換,具體的邏輯不再贅述,再將bean轉(zhuǎn)換為真正需要的類型后,便完成了整個(gè)getBean的使命
Bean銷毀過程
了解了bean的完成創(chuàng)建過程后,那bean是如何銷毀的呢?
bean的創(chuàng)建過程始于DefaultListableBeanFactory.getBean,銷毀過程則終于ConfigurableApplicationContext#close,跟蹤下去,具體的邏輯在DefaultSingletonBeanRegistry#destroySingletons
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry public void destroySingletons() {synchronized (this.singletonObjects) {this.singletonsCurrentlyInDestruction = true;}String[] disposableBeanNames;synchronized (this.disposableBeans) {disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());}for (int i = disposableBeanNames.length - 1; i >= 0; i--) {// 遍歷注冊(cè)的DisposableBeandestroySingleton(disposableBeanNames[i]);}// 清理各種緩存this.containedBeanMap.clear();this.dependentBeanMap.clear();this.dependenciesForBeanMap.clear();clearSingletonCache(); }在介紹bean創(chuàng)建的時(shí)候提到過兩個(gè)概念
需要注冊(cè)銷毀邏輯的bean會(huì)被封裝為DisposableBeanAdapter并緩存在此處
2. DefaultSingletonBeanRegistry.dependentBeanMap
對(duì)于存在依賴注入關(guān)系的bean,會(huì)將bean的依賴關(guān)系緩存在此處(dependentBeanMap: 哪些bean依賴了我; dependenciesForBeanMap: 我依賴了哪些bean)
從上圖中可以看出,bean的銷毀順序與創(chuàng)建順序正好相反,如果有 beanA --dependsOn--> beanB --> beanC ,創(chuàng)建(getBean)時(shí)一定是beanC -> beanB -> beanA,銷毀時(shí)一定是 beanA -> beanB -> beanC,以此避免因?yàn)橐蕾囮P(guān)系造成的一些異常情況
循環(huán)依賴
在介紹Bean創(chuàng)建的時(shí)候提到過earlySingletonObject,為了解決循環(huán)依賴的問題,在實(shí)例化完后屬性注入之前會(huì)提前將當(dāng)前的bean實(shí)體暴露出來,以防止在屬性注入過程中所注入的bean又依賴當(dāng)前的bean造成的類似“死鎖”的狀態(tài),但即便有這樣的邏輯還是要注意幾點(diǎn)
顯示設(shè)置dependsOn的循環(huán)依賴
@DependsOn("beanB") @Component public class BeanA {}@DependsOn("beanC") @Component public class BeanB {}@DependsOn("beanA") @Component public class BeanC {}dependsOn的依賴,在bean的創(chuàng)建之前便會(huì)處理
Spring在實(shí)例化以上bean時(shí),在創(chuàng)建BeanA之前會(huì)觸發(fā)創(chuàng)建BeanB,創(chuàng)建BeanB之前會(huì)觸發(fā)創(chuàng)建BeanC,而創(chuàng)建BeanC之前又會(huì)觸發(fā)創(chuàng)建BeanA,由此引發(fā)一個(gè)無解的循環(huán)依賴
構(gòu)造函數(shù)循環(huán)依賴
@Component public class BeanA {public BeanA(BeanB beanB) {} }@Component public class BeanB {public BeanB(BeanC beanC) {} }@Component public class BeanC {public BeanC(BeanA beanA) {} }與dependsOn一樣的原理,構(gòu)造函數(shù)參數(shù)依賴,同樣在bean的創(chuàng)建之前便會(huì)處理,從而引發(fā)無解的循環(huán)依賴
factory-method依賴
@Bean public BeanA beanA(BeanB beanB) {return new BeanA(); }@Bean public BeanB beanB(BeanC beanC) {return new BeanB(); }@Bean public BeanC beanC(BeanA beanA) {return new BeanC(); }原理與上述相同,不再贅述
顯示dependsOn、構(gòu)造函數(shù)依賴、factory-method依賴任意混合
@DependsOn("beanB") @Component public class BeanA { }@Component public class BeanB {public BeanB(BeanC beanC) {} }@Bean public BeanC beanC(BeanA beanA) {return new BeanC(); }似乎我們找到了一定的規(guī)律,只要一個(gè)循環(huán)依賴中的所有bean,其依賴關(guān)系都需要在創(chuàng)建bean實(shí)例之前進(jìn)行解決,此循環(huán)依賴則一定無解
打破無解的循環(huán)依賴
還以上述三個(gè)Bean為例,先將其中任意一個(gè)依賴設(shè)置為屬性依賴(屬性依賴的處理,在bean實(shí)例創(chuàng)建完成且暴露earlySingleton之后)
@Component public class BeanA {@Autowiredprivate BeanB beanB; }@Component public class BeanB {public BeanB(BeanC beanC) {} }@Bean public BeanC beanC(BeanA beanA) {return new BeanC(); }或
@DependsOn("beanB") @Component public class BeanA { }@Component public class BeanB {private BeanC beanC;@Resourcepublic void setBeanC(BeanC beanC) {this.beanC = beanC;} }@Bean public BeanC beanC(BeanA beanA) {return new BeanC(); }等等
為了避免無解的循環(huán)依賴,在構(gòu)成循環(huán)依賴的一個(gè)環(huán)中,只需要保證其中至少一個(gè)bean的依賴在該bean創(chuàng)建且暴露earlySingleton之后處理即可
我們以“bean創(chuàng)建且暴露earlySingleton”為節(jié)點(diǎn),在此之前處理依賴的有instance supplier parameter、factory method parameter、constructor parameter、等,在此之后處理的依賴有 class property、setter parameter、等
小結(jié)
本文介紹了Spring體系內(nèi)bean的創(chuàng)建及銷毀過程,在經(jīng)過萬次的commit后,也造就了Spring一定程度上的復(fù)雜度
本文并未全方位的詮釋bean的創(chuàng)建過程,文中遺留了很多疑問點(diǎn),同時(shí)也能發(fā)現(xiàn)Spring提供了眾多的擴(kuò)展點(diǎn)來增強(qiáng)Ioc的能力,讓開發(fā)者能夠更好的使用/駕馭
下一篇文章,將著重介紹Spring本文中遺留的疑問點(diǎn)及Spring所提供的各種擴(kuò)展能力
附錄
中間態(tài)說明
林中通幽徑,深山藏小舍
總結(jié)
以上是生活随笔為你收集整理的不一样 使用别名 数据字段和bean_【修炼内功】[spring-framework] [3] Bean是如何创建又是如何销毁的?...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: access 文本转换数字_LabVIE
- 下一篇: 写一个sql实现以下查询结果_SQL复杂