以前十一假期,基於SpringBoot實現集成公司業務和通用封裝的starter,有點相似支付寶的Sofa-Boot。在實現過程當中,不斷優化的過程發現對源碼理解很差,starter很容易寫的不那麼「聰明」。因此就趁着假期一點點跟着源碼閱讀了起來,今天來分享一篇總結簡單Bean的生命週期。html
我閱讀源碼的過程當中,一般分兩步走:跳出來,看全景;鑽進去,看本質。先了解Bean的生命週期圖對其有大致瞭解,該技術在大腦中有了一個「簡單」的構圖後。再閱讀相關源碼去確認本身在看全景時的大腦構圖是否合理。這個過程其實也適用於面對新環境,先熟悉本身所處項目的業務,再過技術棧與代碼相關細節。鑽進去看本質的過程每每是「煎熬的」,須要堅持與不斷的思考。git
Spring源碼版本: 5.0.9.RELEASEgithub
DefaultListableBeanFactory
| preInstantiateSingletons(726) --> getBean(759);
AbstractBeanFactory
| getBean(198) --> doGetBean(199)
| doGetBean(239) --> getSingleton(315)
DefaultSingletonBeanRegistry
| getSingleton(202) --> singletonFactory.getObject(222)
| getSingleton(315) --> createBean(317)
AbstractAutowireCapableBeanFactory
| createBean(456) --> doCreateBean(495)
| doCreateBean(526) --> createBeanInstance(535) return BeanWrapper
| doCreateBean(526) --> addSingletonFactory(566) 加入到三級緩存SingletonFactories
| doCreateBean(526) --> populateBean(572)
| doCreateBean(526) --> initializeBean(573)
| initializeBean(1678) --> invokeAwareMethods(1686)
| invokeAwareMethods(1709) --> ((BeanNameAware) bean).setBeanName(1712)
| invokeAwareMethods(1709) --> ((BeanClassLoaderAware) bean).setBeanClassLoader(1717)
| invokeAwareMethods(1709) --> ((BeanFactoryAware) bean).setBeanFactory(1721)
| initializeBean(1678) --> applyBeanPostProcessorsBeforeInitialization(1691) 這裏面一般調用的是AwareProcessor
| initializeBean(1678) --> invokeInitMethods(1695)
| invokeInitMethods(1695) --> ((InitializingBean) bean).afterPropertiesSet(1758)
| invokeInitMethods(1695) --> invokeCustomInitMethod(1767)
| initializeBean(1678) --> applyBeanPostProcessorsAfterInitialization(1703)
複製代碼
上面的類繼承圖是我簡化過的,其實真正的Spring的BeanFacotory類泛化體系遠比此複雜多。spring
@Slf4j
@Component
public class ComponentA implements InitializingBean, DisposableBean, BeanFactoryAware, ApplicationContextAware {
@Autowired
private ComponentB componentB;
@Value("${ca.test.val:default}")
private String valA;
@Override
public void destroy() {
log.info("$destroy()...");
}
@Override
public void afterPropertiesSet() {
log.info("$afterPropertiesSet()...");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("$setApplicationContext() applicationContext:{}", applicationContext);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
log.info("$setBeanFactory() beanFactory:{}", beanFactory);
}
}
複製代碼
@Component
public class ComponentB {
@Autowired
private ComponentA componentA;
}
複製代碼
@SpringBootApplication
public class EasyUcApplication {
public static void main(String[] args) {
/**
* 使用SpringBoot-2.0.5.RELEASE去啓動Spring容器,對應就是須要的Spring-5.0.9.RELEASE。
*/
SpringApplication.run(EasyUcApplication.class, args);
}
}
複製代碼
由於這塊是循環遍歷全部
BeanDefinitionNames
,F9
找到上面咱們想要看到初始化過程的Bean。下文提到的全部F7
、F8
、F9
分別表明進入方法內部、跳到下一行、跳到下一個斷點處。apache
F8
按照索引目錄跟到759行 設計模式
F7
進入此方法,進入到抽象類AbstractBeanFactory
緩存
F7
進入doGetBean方法 bash
DefaultSigletonBeanRegistry
中的getSingleton
Map
:
這三級緩存具體做用咱們在populate
階段再分析架構
返回到AbstractBeanFactory
的doGetBean(246)
,F8
接着跟到315行 app
F7
跟進去,咱們看到到第222行調用了getObject()
getObject()
F7
跟進去其實調用的就是createBean
F7
跟進去,到了AbstractAutowireCapableBeanFacotory
的createBean(456)
, 在這個方法內找到495行, F7
進入此方法
AbstractAutowireCapableBeanFacotory
的doCreateBean(526)
行,F8
一直到535行,F7
進入此方法。protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
/*省略代碼*/
// 調用有Autowire的構造方式: 就是說構造方法的成員變量是須要注入值的。
Class<?> beanClass = resolveBeanClass(mbd, beanName);
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// 簡單的無參構造器:咱們這裏會走到這種方式
return instantiateBean(beanName, mbd); //1128
}
複製代碼
F7
進入到上面的instantiateBean(1128)
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
/*省略代碼*/
// 1.實例化對象
Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
// 2.建立該對象的BeanWrapper
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
複製代碼
上面出現個BeanWrapper,它的做用是幹什麼的呢?
setPropertyValue
去進行賦值,BeanWrapper
提供一種能夠直接對屬性賦值的能力。以下面所示:Class<?> clazz = Class.forName("cn.coderjia.easyuc.component.ComponentA");
Constructor<?> ctor = clazz.getDeclaredConstructor();
Object bean = ctor.newInstance();
BeanWrapper beanWrapper = new BeanWrapperImpl(bean);
beanWrapper.setPropertyValue("valA", "val");
複製代碼
F8
返回AbstractAutowireCapableBeanFacotory
的doCreateBean(535)
, 而後F8
到559行。
F8
跟到566行
F7
進入此方法,到了DefaultSingletonBeanRegistry
singletonFactories
,目的爲了解決循環引用的問題,那麼什麼是循環引用的狀況與如何解決的呢?且看下文分解。ComponetA
依賴ComponentB
,ComponentB
又依賴於ComponentA
,Spring
中Bean
的實例化Bean
對象後,會對屬性值注入(populate
),這樣就會出現如上圖的循環調用過程(instantiate A
->populate ComponentB
->instantiateB
->populate ComponentA
->instantiate A
)。Bean
對象工廠放置到第三級緩存中。F7
進入到getEarlyBeanReference(566)
earlySingletonObjects
緩存中,在須要提早引用的地方取出就能夠解決循環引用的問題。可是第三級緩存存在的意義除了保存Bean
外,還能夠在獲取的時候,對Bean
能夠作前置處理(InstantiationAwareBeanPostProcessor
),有這樣一種拓展的能力。
Component A
後,把Component A
緩存起來,注入Component B
屬性時,建立Component B
,Component B
須要注入Component A
,從緩存把Component A
取出來,完成Component B
的建立全過程,在將Component B
返回給Component A
,Component A
也可完成建立全過程。F8
跟到populateBean(572)
Componet B
,而後天然而然須要建立Componet B
。接下來看下是否是這樣。F7
進入此方法,到了AbstractAutowireCapableBeanFactory
的populateBean(1277)
行
F8
一直往下跟1339行打個斷點,而後一直F9
跳到bp
爲AutowiredAnnotationBeanPostProcessor
時候中止,F8
跟到 1341行
F7
進入此方法,來到CommonAnnotationBeanPostProcessor
的metadata.inject(318)
行
F7
進入此方法,來到InjectionMetadata
的inject(81)
componetB
和valA
已經在checkoutedElements
中,要對其進行值的注入。能夠發現@Autowired和@Value值就是在這階段對其賦值的。那麼componetB
和valA
何時加到checkoutedElements
中的呢?AbstractAutowireCapableBeanFactory
的applyMergedBeanDefinitionPostPorcessors(547)
行,打個斷點
F9
一直跳到ComponetA
F7
跟進,進入到AbstractAutowireCapableBeanFactory
的applyMergedBeanDefinitionPostPorcessors(1009)
行
1011
行打個斷點,而後F9
一直跳到bp
爲AutowiredAnnotationBeanPostProcessor
F7
進入AutowiredAnnotationBeanPostProcessor
的postProcessMergedBeanDefinition(231)
行
F8
跟到232行,F7
進入此方法,來到了AutowiredAnnotationBeanPostProcessor
的findAutowiringMetadata(405)
行
F8
跟到AutowiredAnnotationBeanPostProcessor
的buildAutowiringMetadata(417)
行
F7
進入此方法,來到了AutowiredAnnotationBeanPostProcessor
的buildAutowiringMetadata(425)
行,而後在433行打個斷點
F9
一直跳到ComponetB
或者valA
F7
進入此方法,來到了AutowiredAnnotationBeanPostProcessor
的findAutowiredAnnotation(480)
行
field
,而後找看看有沒有咱們「感興趣」的field
,「感興趣」的註解是提早定義好的,放在autowiredAnnotationTypes
中。autowiredAnnotationTypes
中都有哪些註解,咱們找到AutowiredAnnotationBeanPostProcessor
的構造方法。
@Autowired
、@Value
、@Inject
,因此ComponetB
或者valA
會最終被放置到checkedElements
中。InjectionMetadata
的inject(81)
,接着分析注入的過程
ComponetB
或者valA
F7
進入InjectionMetadata
的inject(90)
行,來到AutowiredAnnotationBeanPostProcessor
的inject(570)
行
F8
跟到583行,F7
進入beanFactory.resolveDependency(583)
行,來到了DefaultListableBeanFacotory
的doResolveDependency(1062)
行
F7
進入doResolveDependency(1062)
,來到DefaultListableBeanFacotory
的1069行,接着在1135行打個斷點,F9
跳到此位置
F7
進入descriptor.resolveCandidate(1135)
行,來到DependencyDescriptor
的resolveCandidate(248)
行
F7
進入beanFactory.getBean(251)
行,一直跟到AbstractBeanFacotory
的doGetBean(239)
行,如下過程都是建立Componet B
的過程,因此彙集在一塊兒,等Componet B
中須要注入Component A
時咱們在分開。F9
跳到這
F7
進入,F8
跟到doCreateBean(495)
行
F7
進入,F8
跟到doCreateBean(572)
行
populate ComponentA
,F7
進入populateBean
方法。populate ComponentA
時,確定最終會去試圖建立ComponentA
,也會回到AbstractBeanFacotry
的doGetBean(239)
行
F7
進入getSingleton(246)
行,一直跟到DefaultSingletonBeanRegistry
的getSingleton(176)
行,F8
跟到185行
cache
中取出Bean
,並且這個Bean
正好是咱們上文書所說的那樣,是實例化後的,未完成屬性注入和初始化的。F8
到最後doGetBean(392)
行
F8
跟到AutowiredAnnotationBeanPostProcessor
的inject(611)行
ComponetB
建立完成,注入的是預先緩存好的示例化的ComponetA
F8
跟回到AbstractAutowireCapableBeanFacotry
的doCreateBean(573)
行
ComponetB
對於ComponetA
已經注入完成,573行是執行ComponetB
初始化操做,咱們主要看ComponetA
的生命週期,因此能夠直接F9
跳事後面過程回到ComponetA
的populateBean
F9
跳回到ComponetA
的populateBean
Component A
完全完成初始化和屬性注入的過程,並且控制檯沒有任何輸出也符合咱們生命週期圖的預測。F7
進入AbstractAutowireCapableBeanFacotry
的doCreateBean(573)
行來到initializeBean(1678)
行
F7
進入invokeAwareMethods(1686)
行
ComponetA implements BeanFactoryAware
, 因此這塊會回調setBeanFactory
F8
執行完invokeAwareMethods
$setBeanFactory()...
F8
跳回到initializeBean(1691)
行
F7
跟進去,到了AbstractAutowireCapableBeanFacotry
的applyBeanPostProcessorsBeforeInitialization(411)
行
*AwareProcessor
且是在invokeAwareMethods
後面執行的,符合咱們生命週期圖,第二個是ApplicationContextAwareProcessor
是默認給咱們全部bean都建立的*AwareProcessor
,咱們來看下這個ApplicationContextAwareProcessor
是幹什麼的。F7
進入applyBeanPostProcessorsBeforeInitialization(416)
行,來到了ApplicationContextAwareProcessor
的postProcessBeforeInitialization(79)
行,F8
一直跟到invokeAwareInterfaces(96)
行
F7
進入invokeAwareInterfaces(96)
行,來到了invokeAwareInterfaces(102)
行
ApplicationContextAwareProcessor
,若是咱們實現了這些接口,它就會回調。咱們的示例代碼中ComponentA implements ApplicationContextAware
,因此應該會執行ComponentA的setApplicationContext
F8
跟到invokeAwareInterfaces(102)
這個方法的最後,看下打印結果。
*Aware
接口,生命週期要早於*AwareProcessor
。AbstractAutowireCapableBeanFacotry
的initializeBean(1678)
中的invokeInitMethods(1695)
行,F7
進入此方法,再F8
跟到invokeInitMethods(1758)
行
F8
執行afterPropertiesSet
,由於ComponentA implements InitializingBean
,因此控制檯確定會打印$afterPropertiesSet()...
,以下圖所示:
F8
下面的1767行就是執行,目標init方法
F8
跟回到AbstractAutowireCapableBeanFacotry
的applyBeanPostProcessorsAfterInitialization(1703)
行
F8
跟到AbstractAutowireCaplableBeanFacotry
的doCreateBean(614)
行
Bean
註冊爲DisposableBean
會隨着容器
銷燬而銷燬。F8
一直跟回到DefaultSingletonBeanRegistry
的getSingleton(248)
行
F7
進入到此方法,看看建立Bean的最後一步
earlySingletonObjects
轉存到以及緩存singletonObjects
,以上過程咱們完成了Bean
的整個建立過程,Spirng
之因此處理的這麼「鬆散」(低耦合),其實就是在Bean
整個生命週期過程當中,提供給用戶更好的拓展能力。結合本身的業務場景,靈活定製。Runtime.getRuntime().addShutdownHook(Thread hook)
,寫段測試代碼。
System.exit(0)
退出,就會回調是添加的Hook Thread
,Spring
也是這樣作的。Spring
的鉤子線程,找到SpringApplication
的run(333)
行,進入refreshContext(333)
行,來到SpringApplication
的415行,F7
進入此方法,來到AbstractApplicationContext
的registerShutdownHook(930)
行
鉤子
當系統退出時調用doClose()
方法,這段小內容還要碎碎念個事兒,SpringBoot
咱們知道他幫咱們自動裝配了不少配置,其實本質是在spring.factories
定義好須要自動裝配的類,把定義好的類包裝成Spring
須要的BeanDefinition
,剩下的事兒就交給Spring
就行了。固然SpringBoot
還有本身完整的事件發佈體系(觀察者模式
),讓整個SpringBoot
的代碼結構看起來很是清晰。這些話雖然有些跑題,可是這就是我爲何搞SpringBoot starter
卻進入了Spring
的世界。
後臺線程
存在,只能使用System.exit(0)
或者Runtime.getRuntime().exit(0)
,那麼咱們稍作修改代碼。
AbstractApplicationContext
的doClose(1017)
行打個斷點
F7
跟進到DefaultSingletonBeanRegistry
的destroySingletons(491)
行,在504行打個斷點。咱們上文說到ComponentA
註冊爲DisposableBean
,F9
跳到ComponetA
。F7
進入此方法,F8
跟到543行,
F7
進入此方法,F8
跟到destroyBean(571)
行並F8
執行
整篇文章不只是想從表面瞭解
Bean
的世界,而是要走進它的世界。我在寫這篇文章的時候,常常問本身這地方你懂了嗎?答案模棱兩可就是不懂,就須要去仔細品味源碼。讀源碼的做用究竟是什麼?它會讓你見識到一個頂級項目總體的架構,應用了哪些設計模式,一些問題的處理方式等等。這樣在平常工做中就能夠「借鑑」,讓本身的代碼是充滿着「溫度」的好代碼。