本文已收錄 【修煉內功】躍遷之路
書接上文,在 BeanDefinitionReader 一文中簡單介紹了 XMLBeanFactory解析xml配置、並註冊 BeanDefinition的邏輯,本文就bean的實例化過程及銷燬作簡要分析
先放一張大圖(點擊圖片放大查看,右鍵或長按保存後更清晰),展現完整的bean建立過程及銷燬過程,若是對spring原理有一些理解可將此圖做爲開發過程當中的參考,若是對spring原理還有一些模糊可繼續向下閱讀(長文預警!)java
經過前文簡單瞭解到,Spring在初始化過程當中並非直接實例化bean,而是先收集全部bean的元數據信息並註冊,bean的元數據描述爲接口BeanDefinition
,該接口定義了你能想到的一切有關bean的屬性信息web
BeanDefinition衍生出一系列實現類spring
如同其餘Spring類,大部分BeanDefinition接口的邏輯都由該抽象類實現sql
GenericBeanDefinition是一站式、用戶可見的bean definition,如何理解「用戶可見」?數據庫
可見的bean definition意味着能夠在該bean definition上定義post-processor來對bean進行操做segmentfault
當bean definition存在父子關係的時候,RootBeanDefinition用來承載父元數據的角色(也可獨立存在),同時它也做爲一個可合併的bean definition使用,在Spring初始化階段,全部的bean definition均會被(向父級)合併爲RootBeanDefinition,子bean definition(GenericBeanDefinition/ChildBeanDefinition)中的定義會覆蓋其父bean definition(由parentName指定)的定義緩存
當bean definition存在父子關係的時候,ChildBeanDefinition用來承載子元數據的角色(也可獨立存在),在Spring推出GenericBeanDefinition後,其徹底能夠被GenericBeanDefinition替代,目前使用場景已經很是少session
AnnotatedBeanDefinitionapp
如其名,主要用來定義註解場景的bean definition框架
主要用來定義@Component、@Service等bean definition,其AnnotationMetadata metadata屬性用來存儲該bean的類註解信息
與ScannedGenericBeanDefinition不一樣的是,其主要用來定義@Configuration等配置類中@Bean的bean definition,其AnnotationMetadata metadata屬性與ScannedGenericBeanDefinition相同,MethodMetadata factoryMethodMetadata屬性用來存儲@Bean描述的方法信息
BeanDefinitionHolder只是簡單捆綁了BeanDefinition、bean-name、bean-alias,用於註冊BeanDefinition及別名alias
在通常工程中,bean的定義分散在各類地方(尤爲使用註解以後),Spring並不能在解析出每個bean的元數據信息後當即對該bean作實例化動做,對於依賴的分析與注入、類(方法)的代理、功能上的擴展等,必須等全部的bean元數據所有解析完成以後才能進行
在bean元數據解析完成以後、bean實例化以前,對bean的元數據信息有一個暫存的過程,這個過程即是bean的註冊
bean的註冊邏輯分兩步,一爲BeanDefinition的註冊,一爲別名的註冊
在完成bean的元數據註冊以後,即是根據詳盡的元數據信息進行實例化了
bean的實例化過程比較複雜(Spring考慮到了各類場景),附上BeanRegistry&BeanFactory相關的類依賴圖
初看此圖,請不要失去信心和耐心,圖中各種的做用會一一講解(見 #附錄#相關類說明),這裏先介紹幾個核心的接口
bean別名註冊和管理
bean元數據註冊和管理
單例bean註冊和管理
bean工廠,提供各類bean的獲取及判斷方法
大體瀏覽上圖(類依賴圖)不難發現,核心實現落在了DefaultListableBeanFactory
,咱們以此類做爲切入點分析bean實例化過程
bean的實例化過程發生在getBean
調用階段(對於singleton則發生在首次調用階段),getBean的實現方法衆多,咱們追根溯源,找到最通用的方法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. 獲取真正的beanName final String beanName = transformedBeanName(name); Object bean; // 2. 嘗試獲取(提早曝光的)singleton bean實例(爲了解決循環依賴) Object sharedInstance = getSingleton(beanName); // 3. 若是存在 if (sharedInstance != null && args == null) { ... } // 4. 若是不存在 else { ... } // 5. 嘗試類型轉換 if (requiredType != null && !requiredType.isInstance(bean)) { ... } return (T) bean; }
bean的實例化過程雖然複雜,但大致邏輯很是清楚
接下,就以上五個子流程(藍色部分)一一展開
在實例化bean的過程中,Spring會使用大量的中間態來判斷、處理各類場景和狀況,此處先行列出Spring所使用的一些關鍵的中間態(各中間態的做用會在下文介紹,見 #附錄#中間態說明),以便在下文中更好地理解bean實例化過程當中對各類狀況的判斷和處理邏輯
在使用bean-name獲取bean的時候,除了可使用原始bean-name以外,還可使用alias別名等,bean-name的轉換則是將傳入的‘bean-name’一層層轉爲最原始的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)); }
函數canonicalName的做用則是利用別名註冊aliasMap,將別名alias轉爲原始bean-name
函數transformedBeanName比較特殊,其是將FactoryBean的bean-name前綴 '&' 去除(BeanFactory#FACTORY_BEAN_PREFIX 下文會介紹)
拿到原始的bean-name以後,即可以實例化bean或者直接獲取已經實例化的singleton-bean,此處爲何叫‘嘗試’獲取呢?
在獲取singleton-bean的時候通常存在三種狀況:1. 還未實例化(或者不是單例);2. 已經實例化;3. 正在實例化;
Spring中對於singleton-bean,有一個sharedInstance的概念,在調用getSingleton
函數時,返回的不必定是徹底實例化的singleton-bean,有多是一箇中間狀態(建立完成,但未進行屬性依賴注入及其餘後處理邏輯),這種中間狀態會經過getSingleton函數提早曝光出來,目的是爲了解決循環依賴(下文會詳細介紹循環依賴)
在實例化 beanA的過程當中,須要依賴 beanB和 beanC,若是 beanC同時又依賴 beanA,則須要 beanA在實例化完成以前提早曝光出來,以避免形成 beanA等待 beanC實例化完成, beanC等待 beanA實例化完成,相似一種死鎖的狀態
在繼續進行以前,有必要簡單介紹幾個中間態(詳見 #附錄#中間態說明)
緩存已經實例化完成的singleton-bean
緩存正在實例化的、提早曝光的singleton-bean,用於處理循環依賴
緩存用於生成earlySingletonObject的 ObjectFactory
ObjectFactory,定義了一個用於建立、生成對象實例的工廠方法
@FunctionalInterface public interface ObjectFactory<T> { T getObject() throws BeansException; }
介紹了上述以後,再來描述getSingleton
的邏輯就會比較清楚
不用糾結上述中間態的值是什麼時候被設置進去的,下文會逐步說起
上述 sharedInstance 必定是咱們須要的bean實例麼?未必!
定義bean的時候能夠經過實現FactoryBean接口來定製bean實例化的邏輯,以此增長更多的靈活性及可能性(How to use the Spring FactoryBean?)
@Bean(initMethod = "init", destroyMethod = "destroy") public FactoryBean myBean() { return new FactoryBean<MyBean>() { /** * 定製bean初始化邏輯 */ @Override public MyBean getObject() throws Exception { MyBean myBean = new MyBean(); // ... 定製化的邏輯 return myBean; } /** * 真正的bean類型 */ @Override public Class<?> getObjectType() { return MyBean.class; } @Override public boolean isSingleton() { return true; } } }
經過註冊FactoryBean類型的bean,實例化後的原始實例類型一樣爲FactoryBean,但咱們須要的是經過FactoryBean#getObject方法獲得的實例,這便須要針對FactoryBean作一些處理,這也是接下來要講解的函數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.
該函數要實現的邏輯比較簡單,若是sharedInstance是 FactoryBean,則使用getObject
方法建立真正的實例
getObjectForBeanInstance是一個通用函數,並不僅針對經過 getSingleton獲得的 sharedInstance,任何經過緩存或者建立獲得的 rawInstance,都須要通過 getObjectForBeanInstance處理,拿到真正須要的 beanInstance
簡單介紹getObjectForBeanInstance函數的入參
/** * @param beanInstance sharedInstance / rawInstance,可能爲FactoryBean * @param name 傳入的未作轉換的 bean name * @param beanName 對name作過轉換後的原始 canonical bean name * @param mbd 合併後的RootBeanDefinition,下文會介紹 */ protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, RootBeanDefinition mbd)
getObjectForBeanInstance函數的處理邏輯
上圖中有一個邏輯判斷,若是入參name以'&' (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的結果會被緩存到factoryBeanObjectCache,對於緩存中不存在或者不是singleton的狀況,會經過FactoryBean#getObject生成(上圖中藍色未展開的邏輯)
Spring並不是簡單的調用FactoryBean#getObject,而是分爲兩部分處理
上圖中doGetObjectFromFactoryBean,主要對getObject方法進行了包裝,判斷是否須要在SecurityManager框架內執行以及對null結果進行封裝(NullBean)
上圖中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,執行後處理動做 Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }
postProcessAfterInitialization函數能夠對現有bean instance作進一步的處理,甚至能夠返回新的bean instance,這就爲bean的加強提供了一個很是方便的擴展方式(能夠思考一下,AOP的代理類是如何生成的)
以上,討論了bean instance存在於緩存中的狀況,若是緩存中不存則須要進行bean的加載
簡單來說,bean的加載/建立分爲三大部分
這裏相似類繼承,子BeanDefinition屬性會覆蓋父BeanDefinition
對於有依賴的狀況,優先遞歸加載依賴的bean
將BeanDefinition轉爲RootBeanDefinition,若是存在父子關係,則進行合併
這裏再也不贅述,能夠參考 AbstractBeanFactory#getMergedLocalBeanDefinition
String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { // 遍歷全部的依賴 for (String dep : dependsOn) { // 檢測循環依賴 if (isDependent(beanName, dep)) { /* throw exception */ } // 註冊依賴關係 registerDependentBean(dep, beanName); // 遞歸getBean,加載依賴bean try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { /* throw exception */ } } }
邏輯很簡單,但這裏涉及到兩個中間態dependentBeanMap、dependenciesForBeanMap
存儲哪些bean依賴了我(哪些bean裏注入了我)
若是 beanB -> beanA, beanC -> beanA,key爲beanA,value爲[beanB, beanC]
存儲我依賴了哪些bean(我注入了哪些bean)
若是 beanA -> beanB, beanA -> beanC,key爲beanA,value爲[beanB, beanC]
理解二者的存儲關係,有助於在閱讀源碼的過程當中理解bean的加載和銷燬順序
if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { // singletonFactory - ObjectFactory try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
這裏涉及兩個比較核心的函數createBean
、getObjectForBeanInstance
根據BeanDefinition的內容,建立/初始化bean instance
上文已經介紹過,主要處理FactoryBean,將FactoryBean轉爲真正須要的bean instance
createBean被包裝在lambda(singletonFactory)中做爲getSingleton的參數,咱們來看getSingleton的實現邏輯
<img src="../notes/BeanFactory/Flowchart.createSingleton.jpg" alt="Flowchart.createSingleton" style="zoom:50%;" />
因此,關鍵的邏輯在createBean函數中,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的建立與singleton bean相似,只是不會緩存建立完成的bean
scope,即做用域,或者能夠理解爲生命週期
上文介紹了singleton-bean及prototype-bean的建立過程,嚴格意義上講以上兩種都是一種特殊的scope-bean,分別對應ConfigurableBeanFactory#SCOPE_SINGLETON及ConfigurableBeanFactory#SCOPE_PROTOTYPE,前者做用域爲整個IOC容器,也可理解爲單例,後者做用域爲所注入的bean,每次注入(每次觸發getBean)都會從新生成
Spring中還提供不少其餘的scope,如WebApplicationContext#SCOPE_REQUEST或WebApplicationContext#SCOPE_SESSION,前者做用域爲一次web request,後者做用域爲一個web session週期
自定義scope的bean實例建立過程與singleton bean的建立過程十分類似,須要實現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函數的lambda參數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實現見org.springframework.web.context.request.SessionScope
WebApplicationContext#SCOPE_REQUEST對應的Scope實現見org.springframework.web.context.request.RequestScope
以上兩種Scope實現都較爲簡單,前者將初始化的bean存儲在request attribute種,後者將初始化的bean存儲在http session中,具體細節請自行查閱源碼
Q: Spring中實現了哪些Scope?又是何時註冊的?
AbstractAutowireCapableBeanFactory#createBean
瞭解bean建立的過程也是一個抽絲剝繭的過程,真正建立的過程封裝在AbstractAutowireCapableBeanFactory#doCreateBean中,而在此以前有一些準備工做,總體流程以下圖
這一步驟用於鎖定bean class,在沒有顯示指定beanClass的狀況下,使用className加載beanClass
在 [[spring-framework] [2] BeanDefinitionReader](https://segmentfault.com/a/11... 一文中有提到過lookup-method及replace-method,該步驟是爲了確認以上兩種配置中的method是否存在
執行InstantiationAwareBeanPostProcessor前處理器(postProcessBeforeInstantiation)
這裏要特別注意的是,若是這個步驟中生成了「代理」bean instance,則會有一個短路操做,直接返回
該bean instance而再也不執行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 { // 建立bean instance Object beanInstance = doCreateBean(beanName, mbdToUse, args); // ... }
Q: InstantiationAwareBeanPostProcessor的使用場景有哪些?Spring有哪些功能使用了InstantiationAwareBeanPostProcessor?它們是在何時註冊的?
真正bean的建立及初始化過程在此處實現,但Spring對bean建立及初始化邏輯的複雜程度徹底超出了本篇文章之承載,這裏只對一些關鍵的邏輯作梳理
AbstractAutowireCapableBeanFactory#createBeanInstance
從上面的流程圖能夠看出,建立bean實體不必定會使用到構造函數,有兩個特殊的方式
AbstractAutowireCapableBeanFactory#obtainFromSupplier
從Spring5開始,多了一種以函數式註冊bean的方式(參考https://www.baeldung.com/spri...)
// 註冊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));
或者
// 構建BeanDefinition BeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(MyService.class, () -> { MyService myService = new MyService(); // 其餘的邏輯 return myService; }); // 註冊BeanDefinition beanFactory.registerBeanDefinition("myService", bd);
經過以上方式註冊的bean,Spring會調用該supplier生成bean實體
AbstractAutowireCapableBeanFactory#instantiateUsingFactoryMethod
ConstructorResolver#instantiateUsingFactoryMethod
在xml配置中還有一種不太常見的bean註冊方式
public class MyHome { // 靜態方法 public static MyHome create() { return new MyHome(); } } public class MyFactory { // 非靜態方法 public MyHome create() { return new MyHome(); } }
<bean id="myFactory" class="com.manerfan.MyFactory"></bean> <!-- 方式一 --> <!-- 使用指定類的靜態方法 --> <bean id="myHome1" class="com.manerfan.MyHome" factory-method="create"></bean> <!-- 方式二 --> <!-- 使用指定bean的非靜態方法 --> <bean id="myHome2" class="com.manerfan.MyHome" factory-method="create" factory-bean="myFactory"></bean>
Spring會經過指定類的指定方法生成bean實體,其中有兩種方式,一種方式(僅指定factory-method)使用指定類的靜態方法生成,另外一種方式(同時指定factory-method和factory-bean)使用指定bean的非靜態方法生成
同時factory-method中還容許傳入一些參數,若是存在同名函數,Spring會根據參數的個數及類型找到匹配的method
public class MyHome { private MyHouse house; private MyCar car; // setters } public class MyFactory { // 攜帶入參 public MyHome create(MyHouse house, MyCar car) { MyHome myHome = new MyHome(); myHome.setHouse(house); myHome.setCar(car) return myHome; } // 同名函數 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"> <!-- 這裏使用的是構造函數參數類型 --> <constructor-arg name="house" ref="house"/> <constructor-arg name="car" ref="car"/> </bean>
這樣的代碼是否是讓你想到了@Configuration中的@Bean,@Bean所修飾方法若是存在參數的話,Spring會經過參數的類型及名稱自動進行依賴注入
@Configuration public class MyFactory { @Bean public MyHome create(MyHouse house, MyCar car) { MyHome myHome = new MyHome(); myHome.setHouse(house); myHome.setCar(car) return myHome; } }
咱們能夠大膽猜想,@Configuration + @Bean的實現方式就是factory-bean + factory-method,在後文介紹Spring的註解體系時會揭曉
使用指定(類)bean的(靜態)方法建立bean實體的邏輯在ConstructorResolver#instantiate(String, RootBeanDefinition, Object, Method, args),而真正的邏輯在SimpleInstantiationStrategy#instantiate(RootBeanDefinition, String, BeanFactory, Object, Method, Object...),其核心的執行邏輯很是簡單,有了方法factoryMethod(factoryBean)及入參args,即可以調用該方法建立bean實體
Object result = factoryMethod.invoke(factoryBean, args);
factoryBean能夠經過beanFactory.getBean獲取到(正是當前在講的邏輯),factoryMethod能夠經過反射獲取到,入參args如何獲取?這便涉及到Spring中的一個重要概念 -- 依賴注入,如何準確的找到依賴的實體並將其注入,即是接下來的重點,這裏涉及到一個很是重要的函數ConstructorResolver#resolvePreparedArguments,該函數的做用是將BeanDefinition中定義的入參轉換爲真是須要的參數(xml中定義的或者註解中定義的),在 BeanDefinitionReader 一文中有過介紹,ref
會被封裝爲RuntimeBeanReference
存儲、value
會被封裝爲TypedStringValue
存儲等等,如何將這些封裝好的存儲類型轉爲真正須要的函數參數,即是ConstructorResolver#resolvePreparedArguments函數的做用
這裏分紅了三大分支
resolveValueIfNecessary
針對BeanMetadataElement,進行值的轉換,其中又會包含特別細的分支,大體以下
AbstractBeanFactory#evaluateBeanDefinitionString
支持Spl表達式解析bean name
BeanDefinitionValueResolver#resolveInnerBean
與createBean函數的邏輯相似,建立一個inner bean
AutowireCapableBeanFactory#resolveDependency
用來處理OptionalBean、LazyBean、AutowireCandidateBean等(詳見下文「註解注入」一節)
BeanDefinitionValueResolver#resolveManagedArray
BeanDefinitionValueResolver#resolveManagedList
BeanDefinitionValueResolver#resolveManagedSet
BeanDefinitionValueResolver#resolveManagedMap
內部遞歸使用resolveValueIfNecessary方法獲取bean並最終封裝成對應的類型
經過BeanDefinitionValueResolver#evaluate(Spel)計算value的值,最終封裝爲Properties
經過BeanDefinitionValueResolver#evaluate(Spel)計算value的值
對於這部份內容,Spring在接下來的發展中可能還會不斷地擴充
AbstractBeanFactory#evaluateBeanDefinitionString
與resolveValueIfNecessary中的RuntimeBeanNameReference一致,支持Spl表達式解析表達式
其餘
InjectionPoint
使用ThreadLocal提供當前bean被注入到的注入點,能夠參考 https://www.baeldung.com/what...
@Bean @Scope("prototype") public Logger logger(InjectionPoint injectionPoint) { // return MyComponent.class return Logger.getLogger(injectionPoint.getMethodParameter().getContainingClass()); } @Component public class MyComponent { @Autowired private Logger logger; }
與resolveValueIfNecessary中的DependencyDescriptor一致,用來處理OptionalBean、LazyBean等
須要留意的是,上述在進行依賴注入的過程當中,都會調用 DefaultSingletonBeanRegistry#registerDependentBean方法,將各bean之間的依賴關係保存起來,如同前文在介紹加載depends-on時一致,bean之間的依賴關係會分別存放在 dependentBeanMap及 dependenciesForBeanMap之中(下文在介紹屬性注入的時候亦是如此),這對於bean銷燬順序的理解起着相當重要的做用
在返回以前還有convertIfNecessary的方法調用,該函數是將上述解析獲得的值轉換爲函數參數真正的類型
爲什麼要轉換?其實上述過程拿到的值並不是真正須要的值,如
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的做用即是將上述解析獲得的值轉換爲函數參數真正的類型Resource
convert的邏輯在TypeConverterDelegate#convertIfNecessary,其內部基本的邏輯爲
將url轉換爲Resource的PropertyEditor對應爲 org.springframework.core.io.ResourceEditor,正是使用ResourceEditor將 字符串"classpath:/init/sql/init.sql」轉爲對應的Resource
Q: Spring默認的PropertyEditor有哪些?又是何時註冊的?
AbstractAutowireCapableBeanFactory#autowireConstructor
ConstructorResolver#autowireConstructor
使用有參構造函數建立bean實例的一個點在於尋找與參數相對應的構造函數(可能定義了多個構造函數),而對於參數的解析和轉換(參數的依賴注入)則與使用factory method同樣,調用ConstructorResolver#resolvePreparedArguments函數進行處理,這裏再也不重複描述
在拿到真實的入參及對應的構造函數後,下一步即是使用構造函數來建立bean實例,但事情貌似也並無那麼簡單
實例化的過程在ConstructorResolver#instantiate,內部並無統一利用反射技術直接使用構造函數建立,而是分爲兩種狀況
一種,沒有設置override-method時,直接使用構造函數建立
一種,在設置了override-method時,使用cglib技術構造代理類,並代理override方法
以上,Spring默認的實例化策略爲CglibSubclassingInstantiationStrategy
AbstractAutowireCapableBeanFactory#instantiateBean
無參構造函數建立bean實例的過程與有參構造函數建立過程徹底一致,只是少了參數的依賴注入,使用默認無參構造函數進行實例化
AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors
在屬性注入以前提供一次機會來對BeanDefinition進行處理,內部執行全部註冊MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法
在閱讀源碼時注意到一個MergedBeanDefinitionPostProcessor的實現類 AutowiredAnnotationBeanPostProcessor,深刻到實現內部AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata,其實現了兩個註解類的解析 @Value 及 @Autowired ,找到註解修飾的Filed或者Method並緩存,具體的邏輯會在屬性注入一節中詳細介紹
Q: Spring註冊了哪些MergedBeanDefinitionPostProcessor?它們都是作什麼用的?又是何時註冊的?
DefaultSingletonBeanRegistry#addSingletonFactory -> AbstractAutowireCapableBeanFactory#getEarlyBeanReference
還記得上文介紹的「嘗試獲取單例」(AbstractBeanFactory.getSingleton)麼?爲了解決循環依賴會將singleton-bean提早暴露出來,暴露的邏輯會封裝爲ObjectFactory(AbstractAutowireCapableBeanFactory#getEarlyBeanReference實現)緩存在DefaultSingletonBeanRegistry.singletonFactories中,在getBean的邏輯getSingleton中會執行ObjectFactory的邏輯將singleton提早暴露
此時暴露的singleton-bean僅完成了bean的實例化,屬性注入、初始化等邏輯均暫未執行
AbstractAutowireCapableBeanFactory#populateBean
在「建立bean實體」小節中介紹了factory method方式及有參構造函數方式的參數注入邏輯,除此以外還有一種注入即是屬性注入
流程圖中兩次出現了InstantiationAwareBeanPostProcessor,還記得在「Bean建立過程」小結中介紹的InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation麼?若是該步驟生成了「代理」bean instance,則會有一個短路操做,直接返回
該bean instance而再也不執行後續的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
// 構建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中進行依賴的解析、轉換並設置到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 @Inject private MyComponent myComponent1; private MyComponent myComponent2; /** * bean ref by setter method */ @Autowired public void setMyComponet2(@Qualifier("myComponent1") MyComponet component) { this.myComponent2 = component } }
各註解的使用能夠參考 https://www.baeldung.com/spri...
在AbstractAutowireCapableBeanFactory#applyPropertyValues以前發現還有一個動做InstantiationAwareBeanPostProcessor#postProcessProperties(是的InstantiationAwareBeanPostProcessor又出現了),在此有兩個實現引發了個人注意AutowiredAnnotationBeanPostProcessor#postProcessProperties及CommonAnnotationBeanPostProcessor#postProcessProperties,咱們來對比兩個實現的內部邏輯
// 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; }
從代碼及流程圖能夠看出,兩種實現的差別僅在InjectionMetadata的查找邏輯,一個個來
AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata的核心邏輯能夠追蹤到AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata
CommonAnnotationBeanPostProcessor#findResourceMetadata的核心邏輯能夠追蹤到CommonAnnotationBeanPostProcessor.buildResourceMetadata
以上,對於不一樣的註解不一樣的方式(屬性/方法),會被封裝爲不一樣的InjectionElement,並最終將全部的InjectionElement封裝在InjectionMetadata中
在找到InjectionElement以後,下一步即是依賴的解析和注入了(InjectionMetadata#inject)
這裏的邏輯無非就是遍歷內部全部的InjectionElement並執行InjectionElement.inject,上面已經介紹,對於不一樣的註解不一樣的方式(屬性/方法),會被封裝爲不一樣的InjectionElement,那不一樣的InjectionElement也會有不一樣的inject邏輯,至此咱們大體能夠理出一個註解注入的大框架
因此,歸根結底,注入的過程在AutowiredFieldElement、AutowiredMethodElement、ResourceElement、等InjectionElement內部,在繼續進行以前有必要了解一下DefaultListableBeanFactory#resolveDependency
還記得上文「建立bean實體」一節中介紹參數的注入時提到的AutowireCapableBeanFactory#resolveDependency麼?該函數正是調用了DefaultListableBeanFactory#resolveDependency,上文並未詳細展開該函數的邏輯實現,其除了處理OptionalBean、及LazyBean以外,咱們比較關心的邏輯在DefaultListableBeanFactory#doResolveDependency
該函數處理了@Value、@Qualifier、@Primary、@Order等的邏輯
@Value的解析有兩個過程,1. StringValueResolver解析(${spring.sql.init} -> classpath:/init/init.sql);2. PropertyEditor轉換(classpath:/init/init.sql -> Resouce);
AutowiredFieldElement無非就是使用DefaultListableBeanFactory#doResolveDependency將依賴的bean解析到,並設置到對應的屬性上
AutowiredMethodElement則是使用DefaultListableBeanFactory#doResolveDependency將參數對應依賴的bean解析到,並執行對應的方法
Q: 咱們是否能夠自定義註解( InstantiationAwareBeanPostProcessor),來實現相似 @Value、 @Autowired 的功能?
AbstractAutowireCapableBeanFactory#applyPropertyValues
還記得在一開始提到的BeanDefinition中的propertyValues麼?(xml中的bean配置、自定義的BeanDefinition,也有可能來自InstantiationAwareBeanPostProcessor#postProcessProperties),至此這一部分的屬性還未注入依賴
PropertyValue中記錄了須要注入的屬性,已經依賴的類型(String、RuntimeBeanReference、等),根據不一樣的類型解析依賴的bean並設置到對應的屬性上(此過程與DefaultListableBeanFactory#doResolveDependency極其類似,再也不贅述)
AbstractAutowireCapableBeanFactory#initializeBean
以上,完成了bean實例的建立和屬性注入,以後還有一些初始化的方法,好比各類Aware的setXxx是如何調用的、@PostConstruct是怎麼調用的?
Q: Aware類有不少,除了上圖中的三種以外,其餘的Aware是何時調用的?Q: @PreDestroy是如何調用的?destroy-method是什麼時候執行的?
Q: AbstractAdvisingBeanPostProcessor都作了什麼?是如何處理AOP代理的?
AbstractBeanFactory#registerDisposableBeanIfNecessary
至此,終於完成了bean實例的建立、屬性注入以及以後的初始化,此後即可以開始使用了
在使用Spring的過程當中常常還會碰到設置銷燬邏輯的狀況,如數據庫鏈接池、線程池等等,在Spring銷燬bean的時候還須要作一些處理,相似於C++中的析構
在bean的建立邏輯中,最後一個步驟則是註冊bean的銷燬邏輯(DisposableBean)
銷燬邏輯的註冊有幾個條件
if (!mbd.isPrototype() && requiresDestruction(bean, mbd))
知足以上條件的bean會被封裝爲DisposableBeanAdapter,並註冊在DefaultSingletonBeanRegistry.disposableBeans中(詳見附錄#中間態說明)
Q: 理解了bean的銷燬註冊邏輯,那bean的銷燬時什麼時候觸發以及如何執行的?
以上,完成了bean的建立、屬性的注入、dispose邏輯的註冊,但得到的bean類型與實際須要的類型可能依然不相符,在最終交付bean以前(getBean)還須要進行一次類型轉換,上文反覆提到過PropertyEditor,此處不例外,使用的既是PropertyEditor進行的類型轉換,具體的邏輯再也不贅述,再將bean轉換爲真正須要的類型後,便完成了整個getBean的使命
瞭解了bean的完成建立過程後,那bean是如何銷燬的呢?
bean的建立過程始於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--) { // 遍歷註冊的DisposableBean destroySingleton(disposableBeanNames[i]); } // 清理各類緩存 this.containedBeanMap.clear(); this.dependentBeanMap.clear(); this.dependenciesForBeanMap.clear(); clearSingletonCache(); }
在介紹bean建立的時候提到過兩個概念
須要註冊銷燬邏輯的bean會被封裝爲DisposableBeanAdapter並緩存在此處
對於存在依賴注入關係的bean,會將bean的依賴關係緩存在此處(dependentBeanMap: 哪些bean依賴了我; dependenciesForBeanMap: 我依賴了哪些bean)
從上圖中能夠看出,bean的銷燬順序與建立順序正好相反,若是有 beanA --dependsOn--> beanB --> beanC ,建立(getBean)時必定是beanC -> beanB -> beanA,銷燬時必定是 beanA -> beanB -> beanC,以此避免由於依賴關係形成的一些異常狀況
在介紹Bean建立的時候提到過earlySingletonObject,爲了解決循環依賴的問題,在實例化完後屬性注入以前會提早將當前的bean實體暴露出來,以防止在屬性注入過程當中所注入的bean又依賴當前的bean形成的相似「死鎖」的狀態,但即使有這樣的邏輯仍是要注意幾點
@DependsOn("beanB") @Component public class BeanA {} @DependsOn("beanC") @Component public class BeanB {} @DependsOn("beanA") @Component public class BeanC {}
dependsOn的依賴,在bean的建立以前便會處理
Spring在實例化以上bean時,在建立BeanA以前會觸發建立BeanB,建立BeanB以前會觸發建立BeanC,而建立BeanC以前又會觸發建立BeanA,由此引起一個無解的循環依賴
@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同樣的原理,構造函數參數依賴,一樣在bean的建立以前便會處理,從而引起無解的循環依賴
@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("beanB") @Component public class BeanA { } @Component public class BeanB { public BeanB(BeanC beanC) { } } @Bean public BeanC beanC(BeanA beanA) { return new BeanC(); }
彷佛咱們找到了必定的規律,只要一個循環依賴中的全部bean,其依賴關係都須要在建立bean實例以前進行解決,此循環依賴則必定無解
還以上述三個Bean爲例,先將其中任意一個依賴設置爲屬性依賴(屬性依賴的處理,在bean實例建立完成且暴露earlySingleton以後)
@Component public class BeanA { @Autowired private 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; @Resource public void setBeanC(BeanC beanC) { this.beanC = beanC; } } @Bean public BeanC beanC(BeanA beanA) { return new BeanC(); }
等等
爲了不無解的循環依賴,在構成循環依賴的一個環中,只須要保證其中至少一個bean的依賴在該bean建立且暴露earlySingleton以後處理便可
咱們以「bean建立且暴露earlySingleton」爲節點,在此以前處理依賴的有instance supplier parameter
、factory method parameter
、constructor parameter
、等,在此以後處理的依賴有 class property
、setter parameter
、等
本文介紹了Spring體系內bean的建立及銷燬過程,在通過萬次的commit後,也造就了Spring必定程度上的複雜度
本文並未全方位的詮釋bean的建立過程,文中遺留了不少疑問點,同時也能發現Spring提供了衆多的擴展點來加強Ioc的能力,讓開發者可以更好的使用/駕馭
下一篇文章,將着重介紹Spring本文中遺留的疑問點及Spring所提供的各類擴展能力
所在類 | 屬性 | 類型 | 描述 |
---|---|---|---|
SimpleAliasRegistry | aliasMap | Map<String, String> | Map from alias to canonical name.<br/> 別名alias到原始bean-name的映射 |
DefaultSingletonBeanRegistry | earlySingletonObjects | Map<String, Object> | Cache of early singleton objects: bean name to bean instance<br/> 緩存提早曝光的singleton-bean-instance,主要用於解決循環依賴,存在於該處的實例還未執行屬性依賴注入及其餘後處理邏輯 |
singletonFactories | Map<String, ObjectFactory> | Cache of singleton factories: bean name to ObjectFactory.<br/> 緩存用於生成、獲取earlySingletonObject的工廠方法 | |
singletonObjects | Map<String, Object> | Cache of singleton objects: bean name to bean instance.<br/> 緩存已經實例化的singleton-bean-instance | |
dependentBeanMap | Map<String, Set<String>> | Map between dependent bean names: bean name to Set of dependent bean names.<br/> 若是 beanA -> beanB, beanA -> beanC,key爲beanA,value爲[beanB, beanC] | |
dependenciesForBeanMap | Map<String, Set<String>> | Map between depending bean names: bean name to Set of bean names for the bean's dependencies.<br/> 若是 beanB -> beanA, beanC -> beanA,key爲beanA,value爲[beanB, beanC] | |
singletonsCurrentlyInCreation | Set<String> | Names of beans that are currently in creation.<br/> 緩存正在建立中的singleton-bean-name | |
registeredSingletons | Set<String> | Set of registered singletons, containing the bean names in registration order.<br/> 緩存已經建立的singleton-bean-name | |
disposableBeans | Map<String, Object> | Disposable bean instances: bean name to disposable instance.<br/> 緩存註冊的可銷燬bean(bean的銷燬邏輯) | |
FactoryBeanRegistrySupport | factoryBeanObjectCache | Map<String, Object> | Cache of singleton objects created by FactoryBeans: FactoryBean name to object.<br/> 針對singleton,緩存FactoryBean獲得的真正的singleton-bean-instance |