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