二月份工作復盤


在家辦公的一個月

1.主要工作內(nèi)容

1.1 簡化Feign客戶端的初始化:團隊原本使用的是GitHub下的open-feign項目實現(xiàn)的微服務之間的遠程調(diào)用,使用Feign進行調(diào)用的時候采用原的Feign客戶端聲明方式,在配置中聲明一個客戶端@Bean,利用Feign.builder()創(chuàng)建一個Feign的客戶端進行http的遠程調(diào)用,如:

...

@Bean

public LebronJames lebronJames() {

? ? return Feign.builer().decoder(decoder).encoder(encoder).requestInterceptor(interceptor).

? ? ? ? target(LebronJames.class,targetUrl);

}

...

每個調(diào)用者都需要在項目中聲明若干個這樣的Feign客戶端并引入相關的一些配置項和一些配置文件,這樣增加了開發(fā)的復雜性,因此設計一個方法簡化Feign的調(diào)用方式。

1.2 搭建一個前端腳手架,這里主要是為了以后團隊每個微服務提供一個獨立的前端頁面使用,即每個獨立的微服務架構的基礎服務擁有自己的管理頁面,不在統(tǒng)一的頁面進行維護。

這里領導要求使用nodejs技術棧進行搭建,因此我就選擇了element-ui + vue + vue-router + axios + webpack進行搭建,由于之前有過vue開發(fā)的經(jīng)歷,所以這個工作應該是很簡單的哈哈。

2. 工作思路

2.1 利用Spring生命周期完成Feign客戶端的聲明和注入

對于第一個任務,其實這里有個坑,因為最初搭建微服務的時候技術選型是我做的,在使用Feign的時候,最初是有兩個選擇的,一個GitHub的open-feign項目(項目地址:https://github.com/OpenFeign/feign)和SpringCloud生態(tài)系統(tǒng)下的NetFlix Feign,最后種種原因就選擇了GitHub下的open-feign項目,它的初始化就如第一部分所示,采用鏈式builder的方式聲明。

由于是需要將一個接口代理在初始化的時候聲明成一個Bean交給IOC容器管理,很容易就聯(lián)想到了利用Bean的生命周期在Bean初始化的時候完成Feign客戶端的裝配和后面的接口注入,由于項目是基于SpringBoot,所以這里直接實現(xiàn)BeanPostProcessor接口并重寫一下postProcessBeforeInitialization(Object bean,String beanName)方法,在獲取自定義注解的接口代理后完成對應的注入工作。

實現(xiàn)BeanPostProcessor
重寫postProcessBeforeInitialization(Object bean,String beanName)

我們在feignBuilder里完成對Feign客戶端實例的初始化,這里應該注意builder()所使用的其他Bean應該優(yōu)先加載,使用@Order()控制加載順序。

Feign實例的初始化

通過injectFeign()方法完成Bean的注入。

Feign客戶端的注入

至此總得步驟大概完成了。

2.2 搭建Vue腳手架

其實這個比較簡單,網(wǎng)上教程一大堆,在安裝好node環(huán)境后:

1. 命令

npm config set registry https://registry.npm.taobao.org

2. 驗證命令

npm config get registry

如果返回https://registry.npm.taobao.org,說明鏡像配置成功。

3.安裝cnpm

npm install -g cnpm --registry=https://registry.npm.taobao.org

4.安裝vue-cli4

cnpm install -g @vue/cli --> 檢查vue -V

注:windows請使用cmd,不要使用gitbash或者power shell

5.使用vue-cli創(chuàng)建項目

vue create xxxxx -m cnpm

創(chuàng)建項目的時候選擇自定義創(chuàng)建,勾選上vue-router、eslint、axios等組件

6.配置并啟動項目

IDEA中進入terminal選項進入前端項目中,執(zhí)行cnpm install命令安裝依賴。

在IDEA中配置npm,選擇當前項目的package.json,script輸入dev。

修改config下的index.js文件:修改host和port文件。使用localhost作為啟動地址的時候應該修改本地hosts文件,可以使用switchhosts工具。配置proxyTable反向代理地址。

啟動

完成這些后順手寫了幾個組件和頁面,給團隊小伙伴做個參考,非常喜歡寫前端代碼,比后端的好玩哈哈。

3. 知識整理

這里主要整理下Spring中Bean的生命周期吧,以前面試總被問道,步驟很多容易記不住,沒有實際使用也感受不到這個的重要性,通過這次實際使用意識到Bean生命周期的重要性,重新在這里整理下。

這里其實可以將Bean的創(chuàng)建大概分成三個階段,首先是獲取Bean,然后是創(chuàng)建Bean,最后是銷毀Bean。因此這里我們按照三個階段介紹Bean的生命周期。

以下內(nèi)容轉自公眾號:石杉的架構筆記

3.1 獲取Bean


獲取Bean的流程圖

這里的流程圖的入口在AbstractBeanFactory類的doGetBean方法。主要流程就是

1、先處理Bean 的名稱,因為如果以“&”開頭的Bean名稱表示獲取的是對應的FactoryBean對象;

2、從緩存中獲取單例Bean,有則進一步判斷這個Bean是不是在創(chuàng)建中,如果是的就等待創(chuàng)建完畢,否則直接返回這個Bean對象

3、如果不存在單例Bean緩存,則先進行循環(huán)依賴的解析

4、解析完畢之后先獲取父類BeanFactory,獲取到了則調(diào)用父類的getBean方法,不存在則先合并然后創(chuàng)建Bean

3.2 創(chuàng)建Bean

3.2.1?創(chuàng)建Bean之前

在真正創(chuàng)建Bean之前邏輯

這個流程圖對應的代碼在AbstractAutowireCapableBeanFactory類的createBean方法中。

1、這里會先獲取RootBeanDefinition對象中的Class對象并確保已經(jīng)關聯(lián)了要創(chuàng)建的Bean的Class

2、這里會檢查3個條件

(1)Bean的屬性中的beforeInstantiationResolved字段是否為true,默認是false。

(2)Bean是原生的Bean

(3)Bean的hasInstantiationAwareBeanPostProcessors屬性為true,這個屬性在Spring準備刷新容器前調(diào)用BeanPostProcessors的時候會設置,如果當前Bean實現(xiàn)了InstantiationAwareBeanPostProcessor則這個就會是true。

當三個條件都存在的時候,就會調(diào)用實現(xiàn)的InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation方法,然后獲取返回的Bean,如果返回的Bean不是null還會調(diào)用實現(xiàn)的BeanPostProcessor接口的postProcessAfterInitialization方法,這里用代碼說明:

代碼模擬Bean創(chuàng)建方法的調(diào)用

3、如果上面3個條件其中一個不滿足就不會調(diào)用實現(xiàn)的方法。默認這里都不會調(diào)用的這些 BeanPostProcessors的實現(xiàn)方法。然后繼續(xù)執(zhí)行后面的 doCreateBean方法。

3.2.2?真正的創(chuàng)建Bean,doCreateBean

創(chuàng)建Bean的真正流程

doCreateBean方法邏輯

這個代碼的實現(xiàn)還是在AbstractAutowireCapableBeanFactory方法中。流程是

1、先檢查instanceWrapper變量是不是null,這里一般是null,除非當前正在創(chuàng)建的Bean在factoryBeanInstanceCache中存在這個是保存還沒創(chuàng)建完成的FactoryBean的集合。

2、調(diào)用createBeanInstance方法實例化Bean,這個方法在后面會講解

3、如果當前RootBeanDefinition對象還沒有調(diào)用過實現(xiàn)了的MergedBeanDefinitionPostProcessor接口的方法,則會進行調(diào)用

4、 當滿足以下三點

(1)是單例Bean

(2)嘗試解析bean之間的循環(huán)引用

(3)bean目前正在創(chuàng)建中

則會進一步檢查是否實現(xiàn)了SmartInstantiationAwareBeanPostProcessor接口如果實現(xiàn)了則調(diào)用是實現(xiàn)的getEarlyBeanReference方法?

5、 調(diào)用populateBean方法進行屬性填充,這里后面會講解?

6、 調(diào)用initializeBean方法對Bean進行初始化,這里后面會講解

3.2.3?實例化Bean,createBeanInstance

createBeanInstance流程圖

實例化Bean

這里的邏輯稍微有一點復雜,這個流程圖已經(jīng)是簡化過后的了。簡要根據(jù)代碼說明一下流程

createBeanInstance流程圖

1、先檢查Class是否已經(jīng)關聯(lián)了,并且對應的修飾符是否是public的

2、如果用戶定義了Bean實例化的函數(shù),則調(diào)用并返回

3、如果當前Bean實現(xiàn)了 FactoryBean接口則調(diào)用對應的 FactoryBean接口的 getObject方法

4、根據(jù)getBean時候是否傳入構造參數(shù)進行處理

4.1 如果沒有傳入構造參數(shù),則檢查是否存在已經(jīng)緩存的無參構造器,有則使用構造器直接創(chuàng)建,沒有就會調(diào)用 instantiateBean方法先獲取實例化的策略默認是 CglibSubclassingInstantiationStrategy,然后實例化Bean。最后返回

4.2 如果傳入了構造參數(shù),則會先檢查是否實現(xiàn)了 SmartInstantiationAwareBeanPostProcessor接口,如果實現(xiàn)了會調(diào)用 determineCandidateConstructors獲取返回的候選構造器。

4.3 檢查4個條件是否滿足一個

(1)構造器不為null,

(2)從RootBeanDefinition中獲取到的關聯(lián)的注入方式是構造器注入(沒有構造參數(shù)就是setter注入,有則是構造器注入)

(3)含有構造參數(shù)

(4)getBean方法傳入構造參數(shù)不是空

滿足其中一個則會調(diào)用返回的候選構造器實例化Bean并返回,如果都不滿足,則會根據(jù)構造參數(shù)選則合適的有參構造器然后實例化Bean并返回

5、如果上面都沒有合適的構造器,則直接使用無參構造器創(chuàng)建并返回Bean。

3.2.4?填充Bean,populateBean

填充Bean的流程圖

1、檢查當前Bean是否實現(xiàn)了 InstantiationAwareBeanPostProcessor的 postProcessAfterInstantiation方法則調(diào)用,并結束Bean的填充。

2、將按照類型跟按照名稱注入的Bean分開,如果注入的Bean還沒有實例化的這里會實例化,然后放到 PropertyValues對象中。

3、如果實現(xiàn)了 InstantiationAwareBeanPostProcessor類的 postProcessProperties則調(diào)用這個方法并獲取返回值,如果返回值是null,則有可能是實現(xiàn)了過期的 postProcessPropertyValues方法,這里需要進一步調(diào)用 postProcessPropertyValues方法

4、進行參數(shù)填充

3.2.5?初始化Bean,initializeBean

初始化Bean流程圖

初始化Bean

同時這里根據(jù)代碼跟流程圖來說明

1、如果Bean實現(xiàn)了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware則調(diào)用對應實現(xiàn)的方法 。

2、Bean不為null并且bean不是合成的,如果實現(xiàn)了BeanPostProcessor的postProcessBeforeInitialization則會調(diào)用實現(xiàn)的postProcessBeforeInitialization方法。在ApplicationContextAwareProcessor類中實現(xiàn)了postProcessBeforeInitialization方法。而這個類會在Spring刷新容器準備beanFactory的時候會加進去,這里就會被調(diào)用,而調(diào)用里面會檢查Bean是不是EnvironmentAware,EmbeddedValueResolverAware,ResourceLoaderAware,ApplicationEventPublisherAware,MessageSourceAware,ApplicationContextAware的實現(xiàn)類。這里就會調(diào)用對應的實現(xiàn)方法。代碼如下:

初始化Bean流程代碼

1、實例化Bean然后,檢查是否實現(xiàn)了InitializingBeanafterPropertiesSet方法,如果實現(xiàn)了就會調(diào)用

2、Bean不為null并且bean不是合成的,如果實現(xiàn)了BeanPostProcessorpostProcessBeforeInitialization則會調(diào)用實現(xiàn)的postProcessAfterInitialization方法。

到此創(chuàng)建Bean 的流程就沒了,剩下的就是容器銷毀的時候的了

3.3?destory方法跟銷毀Bean

Bean在創(chuàng)建完畢之后會檢查用戶是否指定了 destroyMethodName以及是否實現(xiàn)了 DestructionAwareBeanPostProcessor接口的 requiresDestruction方法,如果指定了會記錄下來保存在 DisposableBeanAdapter對象中并保存在bean的 disposableBeans屬性中。代碼在 AbstractBeanFactory的 registerDisposableBeanIfNecessary中

在銷毀Bean的時候最后都會調(diào)用AbstractAutowireCapableBeanFactory的destroyBean方法。

這里是創(chuàng)建一個 DisposableBeanAdapter對象,這個對象實現(xiàn)了Runnable接口,在實現(xiàn)的 run方法中會調(diào)用實現(xiàn)的 DisposableBean接口的 destroy方法。并且在創(chuàng)建 DisposableBeanAdapter對象的時候會根據(jù)傳入的bean是否實現(xiàn)了 DisposableBean接口來設置 invokeDisposableBean變量,這個變量表實有沒有實現(xiàn) DisposableBean接口

Bean的銷毀

3.4 Bean的生命周期總結


Bean的生命周期總結
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容