之前我們已經(jīng)分析SpringBoot在run方法時控轿,它會執(zhí)行的refresh()容器的操作挽荠。
在SpringBoot中啦扬,refresh()有十幾個方法镀迂,每個方法的大重要程度是不一樣的内狗,我們通過抓大放小的方式怪嫌,分析處理上圖3個核心邏輯。
并且已經(jīng)研究完了invokeBeanFactoryPostProcessors和onRefresh的邏輯柳沙,分析它們的原理和設(shè)計思想岩灭。
之前主要分析的:
原理有對SpringBoot的自動裝配配置如何做到的、第三方技術(shù)如何進(jìn)行擴(kuò)展的赂鲤、tomcat如何啟動的
設(shè)計思想有SpringBoot擴(kuò)展接口設(shè)計噪径、有對Tomcat組件的擴(kuò)展設(shè)計、Spring容器抽象思想的設(shè)計数初、SpringBoot和第三方技術(shù)整合的擴(kuò)展設(shè)計等等找爱。
refresh()還有一個非常關(guān)鍵的操作,就是bean的實例化,今天我們就來看下refresh最后一個方法—finishBeanFactoryInitialization泡孩。
看看它如何執(zhí)行Bean實例化的流程和設(shè)計的车摄。
finishBeanFactoryInitialization之前和之后的操作概況
可以看到,bean的實例化前后,還是做了一些事情的,主要執(zhí)行的是一些擴(kuò)展點吮播,比如listener的擴(kuò)展點執(zhí)行变屁、LifycycleProcessor的執(zhí)行。
這一節(jié)我們核心關(guān)系的是bean創(chuàng)建流程和設(shè)計意狠,所以我們抓大放小粟关,過就可以。直接來看下面創(chuàng)建bean吧环戈。
preInstantiateSingletons方法的核心脈絡(luò)
其實bean的實例化大家或多或少都知道一些誊役。所以我不會特別詳細(xì)的每一個方法都帶大家看。
我們還是本著先脈絡(luò)后細(xì)節(jié)谷市,最后思考的思想來分析Bean的實例化,當(dāng)你用這種方法分析玩后击孩,和你之前分析對吧下有什么區(qū)別迫悠,可以感受下。
如果之后大家有訴求需要精讀Bean實例化的邏輯巩梢,我之后可以考慮放在Spring成長記中為大家仔細(xì)帶來Bean實例化的分析创泄。
這里我們主要過下它的核心源碼就可以了。
由于SpringBoot是為了更好的使用Spring括蝠,它是基于Spring的鞠抑。如果你懂Spring實例化,這塊其實非常好理解的忌警。
讓我們來看下吧!
finishBeanFactoryInitialization主要的代碼如下所示:
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
這個方法的脈絡(luò)其實比較清楚搁拙,其實最關(guān)鍵的只有一句話:
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
其余的都是給beanFactory補(bǔ)充點東西而已,不是很關(guān)鍵法绵。
這句話從注釋很清楚的說了箕速,是根據(jù)BeanDefinition實例化所有剩余的單例非延遲初始化的bean。
整個方法我通過先脈絡(luò)的思想朋譬,給大家概括了下:
public void preInstantiateSingletons() throws BeansException {
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
//遍歷所有beanDefinition盐茎,基于beanDefinition創(chuàng)建bean
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (isFactoryBean(beanName)) {
//做一些處理FactoryBean
//再從容器中獲取bean,如果不存在就創(chuàng)建
getBean(beanName);
}else{
//從容器中獲取bean徙赢,如果不存在就創(chuàng)建
getBean(beanName);
}
}
// 執(zhí)行Bean的擴(kuò)展操作
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
smartSingleton.afterSingletonsInstantiated();
}
}
}
上面的代碼是我對源碼精簡后的邏輯字柠,它的脈絡(luò)非常清晰了整體如下圖所示:
創(chuàng)建bean的核心流程
當(dāng)你知道了preInstantiateSingletons方法的核心脈絡(luò)后,它主要觸發(fā)的是getBean方法狡赐,之后觸發(fā)了doGetBean窑业。
doGetBean整個方法還是比較復(fù)雜的,我還是通過先脈絡(luò)的思想枕屉,抓大放小后数冬,給大家精簡了下源碼,精簡后如下:
@Overrid
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
//嘗試獲取bean,如果容器中有了拐纱,就不需要創(chuàng)建了
Object bean = getSingleton(beanName);
if(bean == nul){
//getFromParentBeanfacotry 當(dāng)前容器沒有bean對應(yīng)的單例對象铜异,嘗試從父容器獲取,如果父容器為空秸架,則不做處理
//默認(rèn)父容器空揍庄,這里略過
//當(dāng)前bean的依賴dependsOn處理,遞歸調(diào)用getBean
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
getBean(dep);
}
// 單例還是多例的方式創(chuàng)建bean
if (mbd.isSingleton()) {
bean = createBean(beanName, mbd, args);
}else if (mbd.isPrototype()) {
bean = createBean(beanName, mbd, args);
}else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
bean = createBean(beanName, mbd, args);
}
}
//bean的擴(kuò)展东抹,使用轉(zhuǎn)換器處理bean
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
return (T) bean;
}
doGetBean整體代碼方法通過蚂子,抓大放小,分析脈絡(luò)后缭黔,其實已經(jīng)很清晰了食茎。主要做了這么幾件事:
1)getSingleton從容器Beanfactory中的map屬性,獲取bean馏谨,如果非空别渔,直接就可以使用
2)如果容器中沒有這個bean,通過判斷是否單例惧互,來執(zhí)行對應(yīng)的創(chuàng)建bean方法createBean
3)如果當(dāng)前bean的依賴dependsOn處理哎媚,遞歸調(diào)用getBean
4)最后執(zhí)行了bean的擴(kuò)展,使用轉(zhuǎn)換器處理bean
doGetBean方法的大體脈絡(luò)喊儡,基本上就是這四步拨与,如下圖所示:
真正創(chuàng)建bean的邏輯,到現(xiàn)在我們還是沒有看到艾猜,需要繼續(xù)向下找买喧。doGetBean之后下面就會執(zhí)行createBean
同理我們使用之前的方法繼續(xù)梳理脈絡(luò)、抓大放小得到如下代碼:
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {匆赃、
//Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
}
這里createBean其實主要執(zhí)行了resolveBeforeInstantiation和doCreateBean方法岗喉。
1)resolveBeforeInstantiation方法從注解上看,是給動態(tài)代理創(chuàng)建一個對象的機(jī)會炸庞,也就說钱床,可以通過BeanPostProcessor使用動態(tài)代理對某些bean直接進(jìn)行創(chuàng)建。這個非常有意思埠居,也很關(guān)鍵查牌,你想想是不是有的技術(shù)就是利用這里進(jìn)行創(chuàng)建的呢?
2)如果不滿足第一個條件滥壕,就會使用doCreateBean來創(chuàng)建Bean
整個邏輯如下圖所示:
這里終于找到了一種bean創(chuàng)建的方式了纸颜,之后應(yīng)該還有其他方式,比如反射绎橘。我們繼續(xù)來看doCreateBean胁孙。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
//創(chuàng)建bean唠倦,bean的實例化
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//bean屬性的填充
populateBean(beanName, mbd, instanceWrapper);
//bean 擴(kuò)展點的觸發(fā)
initializeBean(beanName, exposedObject, mbd);
//bean 擴(kuò)展點的添加
registerDisposableBeanIfNecessary(beanName, bean, mbd);
return exposedObject;
}
整個doCreateBean方法,通過我們之前的思路涮较,一樣精簡完后稠鼻,脈絡(luò)也很清楚。主要有:
1)創(chuàng)建bean狂票,bean的實例化
2)bean屬性的處理
3)bean 擴(kuò)展點的觸發(fā)
4)bean 擴(kuò)展點的添加
doCreateBean的脈絡(luò)如下圖所示:
這里可以看到候齿,除了之前動態(tài)代理的截胡,終于找到了bean實例化闺属,創(chuàng)建bean的地方了慌盯。
其余對bean屬性處理和擴(kuò)展點,我們先不看掂器。重點研究清楚bean的創(chuàng)建再說亚皂。
createBeanInstance同樣被我們抓大放小后的代碼如下:
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
//instanceSupplier方式創(chuàng)建bean
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
//FactoryMethod方式創(chuàng)建bean
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
//反射的方式創(chuàng)建bean
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
return instantiateBean(beanName, mbd);
}
你會發(fā)現(xiàn)有多種創(chuàng)建的bean的方式,并不是只有反射国瓮,方式主要有:
1)instanceSupplier方式創(chuàng)建bean
2)FactoryMethod方式創(chuàng)建bean
3)反射的方式創(chuàng)建bean
那么再算上之前動態(tài)代理創(chuàng)建bean灭必、Factorybean創(chuàng)建的bean。一共已經(jīng)有5種可以創(chuàng)建bean的方式巍膘, 但是一般我們還都是通過反射創(chuàng)建的居多。
你可能沒有見過其他的方式創(chuàng)建的bean芋簿,但是我在一些技術(shù)中也見過一些峡懈,給大家分享下:
比如
Shiro框架使用Factorybean創(chuàng)建的就比較多
動態(tài)代理創(chuàng)建bean、dubbo就有很多這么創(chuàng)建的
FactoryMethod創(chuàng)建的bean与斤,SpringBoot自動裝配的時候有時候會用到肪康,之前tomcat啟動的時候,TomcatServletWebServerFactory是不是就用到了撩穿。
instanceSupplier是Spring5之后才有的磷支,目前我還沒有見到啥框架用到過....
好了不管如何,最終你獲得了如下的一張圖食寡,總結(jié)bean的創(chuàng)建方式:
到這里bean 的創(chuàng)建的流程雾狈,我們就大體分析完了,由于我們不是分析Spring抵皱,所以就不會再對這里面的每個細(xì)節(jié)進(jìn)行分析了善榛。
之后有機(jī)會出Spring成長記的時候,我可以在帶大家詳細(xì)分析吧呻畸。
只是熟悉SpringBoot中移盆,Spring實例化bean的流程了解到這里基本就可以了。
創(chuàng)建的bean整個流程可以總結(jié)下圖的幾步:
Bean實例化的擴(kuò)展點設(shè)計
最后我們來看下Bean擴(kuò)展設(shè)計吧伤为,這個其實網(wǎng)上都有一大堆了咒循,但是你一定要注意,你得會區(qū)分優(yōu)劣的文章、提煉關(guān)鍵點叙甸,要對這些有自己的思考才行颖医。
就像我現(xiàn)在給大家分享的,就是我對擴(kuò)展點的思考蚁署。這個是我一直給大家強(qiáng)調(diào)的便脊。
好了,我來簡單說下光戈,我對Bean的擴(kuò)展點設(shè)計的思考和理解吧哪痰。
在Spring中,Bean實例化的時候久妆,有很多擴(kuò)展點晌杰,這些擴(kuò)展點其實還是很關(guān)鍵的。
比如:在Spring的生態(tài)系統(tǒng)中筷弦,很多技術(shù)都是通過Bean的擴(kuò)展點來實現(xiàn)的肋演。而且包括第三方的技術(shù),比如bytetcc分布式事物框架的實現(xiàn)原理和Bean擴(kuò)展點BeanPostProcessor就有很大的關(guān)系烂琴、大企業(yè)自研框架爹殊,可以實現(xiàn)自定義注解的處理、自定義配置文件的處理奸绷、給自己開發(fā)的bean設(shè)置屬性等等梗夸。
那Bean的擴(kuò)展點設(shè)計了哪些呢?我給大家花了一個圖号醉,基本就能概況常見的擴(kuò)展點了反症,當(dāng)然可能還有一些其他的擴(kuò)展點,不管有多少個畔派,它們都是擴(kuò)展點铅碍,合理利用就好了 ,這個是關(guān)鍵线椰。
Bean實例化時胞谈,常見的擴(kuò)展點的設(shè)計如圖所示:
小結(jié)
如果你去看Bean的實例化的整個流程,其實其中的細(xì)節(jié)很復(fù)雜的憨愉,如果在復(fù)雜中找到關(guān)鍵點呜魄,是SpringBoot成長記以來,一直想要教給大家的莱衩。
最后通過對Bean實例化的分析爵嗅,讓大家熟練的應(yīng)用了之前的學(xué)到的先脈絡(luò)后細(xì)節(jié)、抓大放小笨蚁、連蒙帶猜睹晒、畫核心組件圖趟庄、流程圖、看注釋等思想和方法伪很。
而且每看一陣子邏輯戚啥,要對它做出思考,思考它的設(shè)計锉试,它的擴(kuò)展猫十、它的思想理念等等。
這個是常用的一套方法論呆盖,可能不適合所有場景拖云,但是大多情況可以讓你閱讀源碼或者研究技術(shù)原理的時候,不那么不安应又,不會覺得它們太難宙项,可以讓你有方向、有方法株扛。
今天的內(nèi)容尤筐,其實沒有什么要總結(jié)的,比較重要的就是最后bean 的擴(kuò)展設(shè)計洞就,我就不在重復(fù)了盆繁。
好了,我們下一節(jié)再見旬蟋!
本文由博客一文多發(fā)平臺 OpenWrite 發(fā)布油昂!