spring源碼--bean的生命周期

只有四個阿逃!

是的卧波,Spring Bean的生命周期只有這四個階段时肿。把這四個階段和每個階段對應的擴展點糅合在一起雖然沒有問題,但是這樣非常凌亂港粱,難以記憶螃成。要徹底搞清楚Spring的生命周期,首先要把這四個階段牢牢記住查坪。實例化和屬性賦值對應構造方法和setter方法的注入寸宏,初始化和銷毀是用戶能自定義擴展的兩個階段。在這四步之間穿插的各種擴展點咪惠,稍后會講击吱。

  1. 實例化 Instantiation

  2. 屬性賦值 Populate

  3. 初始化 Initialization

  4. 銷毀 Destruction

實例化 -> 屬性賦值 -> 初始化 -> 銷毀

主要邏輯都在doCreate()方法中,邏輯很清晰遥昧,就是順序調用以下三個方法覆醇,這三個方法與三個生命周期階段一一對應朵纷,非常重要,在后續(xù)擴展接口分析中也會涉及永脓。

  1. createBeanInstance() -> 實例化

  2. populateBean() -> 屬性賦值

  3. initializeBean() -> 初始化

源碼如下袍辞,能證明實例化,屬性賦值和初始化這三個生命周期的存在常摧。關于本文的Spring源碼都將忽略無關部分搅吁,便于理解:

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n24" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">// 忽略了無關代碼
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
?
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
// 實例化階段!
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
?
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 屬性賦值階段落午!
populateBean(beanName, mbd, instanceWrapper);
// 初始化階段谎懦!
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
?

}</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="dart" cid="n40" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">InstantiationAwareBeanPostProcessor extends BeanPostProcessor</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="tsx" cid="n46" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
?
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
// postProcessBeforeInstantiation方法調用點,這里就不跟進了溃斋,
// 有興趣的同學可以自己看下界拦,就是for循環(huán)調用所有的InstantiationAwareBeanPostProcessor
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}

try {
// 上文提到的doCreateBean方法,可以看到
// postProcessBeforeInstantiation方法在創(chuàng)建Bean之前調用
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}

}</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="tsx" cid="n52" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
?
// 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.
boolean continueWithPropertyPopulation = true;
// InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation()
// 方法作為屬性賦值的前置檢查條件梗劫,在屬性賦值之前執(zhí)行享甸,能夠影響是否進行屬性賦值!
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
?
// 忽略后續(xù)的屬性賦值操作代碼
}</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n64" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">Aware Group1</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n72" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">Aware Group2</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="kotlin" cid="n81" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="dart" cid="n86" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;"> // 見名知意梳侨,初始化階段調用的方法
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
?
// 這里調用的是Group1中的三個Bean開頭的Aware
invokeAwareMethods(beanName, bean);
?
Object wrappedBean = bean;

// 這里調用的是Group2中的幾個Aware蛉威,
// 而實質上這里就是前面所說的BeanPostProcessor的調用點!
// 也就是說與Group1中的Aware不同走哺,這里是通過BeanPostProcessor(ApplicationContextAwareProcessor)實現(xiàn)的蚯嫌。
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
// 下文即將介紹的InitializingBean調用點
invokeInitMethods(beanName, wrappedBean, mbd);
// BeanPostProcessor的另一個調用點
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
?
return wrappedBean;
}</pre>

  • 影響多個Bean

    • BeanPostProcessor

    • InstantiationAwareBeanPostProcessor

  • 影響單個Bean

    • Aware

      • Aware Group1

        • BeanNameAware

        • BeanClassLoaderAware

        • BeanFactoryAware

      • Aware Group2

        • EnvironmentAware

        • EmbeddedValueResolverAware

        • ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware)

    • 生命周期

      • InitializingBean

      • DisposableBean

多個擴展點

  • 實例化 Instantiation

  • 屬性賦值 Populate

  • 初始化 Initialization

  • 銷毀 Destruction

Spring Bean的生命周期分為四個階段多個擴展點。擴展點又可以分為影響多個Bean影響單個Bean割坠。整理如下: 四個階段

總結

PriorityOrdered齐帚、Ordered接口作為Spring整個框架通用的排序接口,在Spring中應用廣泛彼哼,也是非常重要的接口对妄。

<pre spellcheck="false" class="md-fences mock-cm md-end-block" lang="dart" cid="n117" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: pre-wrap; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;"> /**
* Useful constant for the highest precedence value.
* @see java.lang.Integer#MIN_VALUE
*/
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;

/**
 * Useful constant for the lowest precedence value.
 * @see java.lang.Integer#MAX_VALUE
 */
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;</pre>

根據排序接口返回值排序,默認升序排序敢朱,返回值越低優(yōu)先級越高剪菱。

<pre spellcheck="false" class="md-fences mock-cm md-end-block" lang="kotlin" cid="n114" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: pre-wrap; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
// 首先,加入實現(xiàn)了PriorityOrdered接口的BeanPostProcessors拴签,順便根據PriorityOrdered排了序
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();

        // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.

// 然后孝常,加入實現(xiàn)了Ordered接口的BeanPostProcessors,順便根據Ordered排了序
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();

        // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.

// 最后加入其他常規(guī)的BeanPostProcessors
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}</pre>

在以下源碼中蚓哩,可以很清晰的看到Spring注冊各種類型BeanPostProcessor的邏輯构灸,根據實現(xiàn)不同排序接口進行分組。優(yōu)先級高的先加入岸梨,優(yōu)先級低的后加入喜颁。

  • PriorityOrdered是一等公民稠氮,首先被執(zhí)行,PriorityOrdered公民之間通過接口返回值排序

  • Ordered是二等公民半开,然后執(zhí)行隔披,Ordered公民之間通過接口返回值排序

  • 都沒有實現(xiàn)是三等公民,最后執(zhí)行

BeanPostProcessor有很多個寂拆,而且每個BeanPostProcessor都影響多個Bean奢米,其執(zhí)行順序至關重要,必須能夠控制其執(zhí)行順序才行纠永。關于執(zhí)行順序這里需要引入兩個排序相關的接口:PriorityOrdered鬓长、Ordered

執(zhí)行順序

可以看出,Spring是先執(zhí)行registerBeanPostProcessors()進行BeanPostProcessors的注冊尝江,然后再執(zhí)行finishBeanFactoryInitialization初始化我們的單例非懶加載的Bean痢士。

<pre spellcheck="false" class="md-fences mock-cm md-end-block" lang="java" cid="n101" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: pre-wrap; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            // 所有BeanPostProcesser初始化的調用點
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            // 所有單例非懶加載Bean的調用點
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

}</pre>

我們知道BeanPostProcessor也會注冊為Bean,那么Spring是如何保證BeanPostProcessor在我們的業(yè)務Bean之前初始化完成呢茂装? 請看我們熟悉的refresh()方法的源碼,省略部分無關代碼:

注冊時機

擴展閱讀: BeanPostProcessor 注冊時機與執(zhí)行順序

  1. InitializingBean 對應生命周期的初始化階段善延,在上面源碼的invokeInitMethods(beanName, wrappedBean, mbd);方法中調用少态。 有一點需要注意,因為Aware方法都是執(zhí)行在初始化方法之前易遣,所以可以在初始化方法中放心大膽的使用Aware接口獲取的資源彼妻,這也是我們自定義擴展Spring的常用方式。 除了實現(xiàn)InitializingBean接口之外還能通過注解或者xml配置的方式指定初始化方法豆茫,至于這幾種定義方式的調用順序其實沒有必要記侨歉。因為這幾個方法對應的都是同一個生命周期,只是實現(xiàn)方式不同揩魂,我們一般只采用其中一種方式幽邓。

  2. DisposableBean 類似于InitializingBean,對應生命周期的銷毀階段火脉,以ConfigurableApplicationContext#close()方法作為入口牵舵,實現(xiàn)是通過循環(huán)取所有實現(xiàn)了DisposableBean接口的Bean然后調用其destroy()方法 。感興趣的可以自行跟一下源碼倦挂。

至于剩下的兩個生命周期接口就很簡單了畸颅,實例化和屬性賦值都是Spring幫助我們做的,能夠自己實現(xiàn)的有初始化和銷毀兩個生命周期階段方援。

簡單的兩個生命周期接口

關于Aware接口的執(zhí)行順序没炒,其實只需要記住第一組在第二組執(zhí)行之前就行了。每組中各個Aware方法的調用順序其實沒有必要記犯戏,有需要的時候點進源碼一看便知送火。

BeanPostProcessor的調用時機也能在這里體現(xiàn)拳话,包圍住invokeInitMethods方法,也就說明了在初始化階段的前后執(zhí)行漾脂。

可以看到并不是所有的Aware接口都使用同樣的方式調用假颇。Bean××Aware都是在代碼中直接調用的,而ApplicationContext相關的Aware都是通過BeanPostProcessor#postProcessBeforeInitialization()實現(xiàn)的骨稿。感興趣的可以自己看一下ApplicationContextAwareProcessor這個類的源碼笨鸡,就是判斷當前創(chuàng)建的Bean是否實現(xiàn)了相關的Aware方法,如果實現(xiàn)了會調用回調方法將資源傳遞給Bean坦冠。 至于Spring為什么這么實現(xiàn)形耗,應該沒什么特殊的考量。也許和Spring的版本升級有關辙浑〖さ樱基于對修改關閉,對擴展開放的原則判呕,Spring對一些新的Aware采用了擴展的方式添加倦踢。

詳情如下,忽略了部分無關代碼侠草。代碼位置就是我們上文提到的initializeBean方法詳情辱挥,這也說明了Aware都是在初始化階段之前調用的!

Aware調用時機源碼分析

這里涉及到另一道面試題边涕,ApplicationContext和BeanFactory的區(qū)別晤碘,可以從ApplicationContext繼承的這幾個接口入手,除去BeanFactory相關的兩個接口就是ApplicationContext獨有的功能功蜓,這里不詳細說明园爷。

  1. EnvironmentAware

  2. EmbeddedValueResolverAware 這個知道的人可能不多,實現(xiàn)該接口能夠獲取Spring EL解析器式撼,用戶的自定義注解需要支持spel表達式的時候可以使用童社,非常方便。

  3. ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware) 這幾個接口可能讓人有點懵端衰,實際上這幾個接口可以一起記叠洗,其返回值實質上都是當前的ApplicationContext對象,因為ApplicationContext是一個復合接口旅东,如下:

  4. BeanNameAware

  5. BeanClassLoaderAware

  6. BeanFactoryAware

Aware類型的接口的作用就是讓我們能夠拿到Spring容器中的一些資源灭抑。基本都能夠見名知意抵代,Aware之前的名字就是可以拿到什么資源腾节,例如BeanNameAware可以拿到BeanName,以此類推。調用時機需要注意:所有的Aware方法都是在初始化階段之前調用的案腺! Aware接口眾多庆冕,這里同樣通過分類的方式幫助大家記憶。 Aware接口具體可以分為兩組劈榨,至于為什么這么分访递,詳見下面的源碼分析。如下排列順序同樣也是Aware接口的執(zhí)行順序同辣,能夠見名知意的接口不再解釋拷姿。

無所不知的Aware
  1. Aware類型的接口

  2. 生命周期接口

這一大類接口的特點是功能豐富,常用于用戶自定義擴展旱函。 第二大類中又可以分為兩類:

第二大類:只調用一次的接口

關于BeanPostProcessor執(zhí)行階段的源碼穿插在下文Aware接口的調用時機分析中响巢,因為部分Aware功能的就是通過他實現(xiàn)的!只需要先記住BeanPostProcessor在初始化前后調用就可以了。

可以看到該方法在屬性賦值方法內棒妨,但是在真正執(zhí)行賦值操作之前踪古。其返回值為boolean,返回false時可以阻斷屬性賦值階段(continueWithPropertyPopulation = false;)券腔。

  • postProcessAfterInstantiation調用點伏穆,忽略無關代碼:

可以看到,postProcessBeforeInstantiation在doCreateBean之前調用纷纫,也就是在bean實例化之前調用的蜈出,英文源碼注釋解釋道該方法的返回值會替換原本的Bean作為代理,這也是Aop等功能實現(xiàn)的關鍵點涛酗。

  • postProcessBeforeInstantiation調用點,忽略無關代碼:
InstantiationAwareBeanPostProcessor源碼分析:

InstantiationAwareBeanPostProcessor實際上繼承了BeanPostProcessor接口偷厦,嚴格意義上來看他們不是兩兄弟商叹,而是兩父子。但是從生命周期角度我們重點關注其特有的對實例化階段的影響只泼,圖中省略了從BeanPostProcessor繼承的方法剖笙。

[圖片上傳失敗...(image-d0ad8d-1589272967303)]

這兩兄弟可能是Spring擴展中最重要的兩個接口!InstantiationAwareBeanPostProcessor作用于實例化階段的前后请唱,BeanPostProcessor作用于初始化階段的前后弥咪。正好和第一、第三個生命周期階段對應十绑。通過圖能更好理解:

  • BeanPostProcessor

  • InstantiationAwareBeanPostProcessor

實現(xiàn)了這些接口的Bean會切入到多個Bean的生命周期中聚至。正因為如此,這些接口的功能非常強大本橙,Spring內部擴展也經常使用這些接口扳躬,例如自動注入以及AOP的實現(xiàn)都和他們有關。

第一大類:影響多個Bean的接口

Spring生命周期相關的常用擴展點非常多,所以問題不是不知道贷币,而是記不住或者記不牢击胜。其實記不住的根本原因還是不夠了解,這里通過源碼+分類的方式幫大家記憶役纹。

常用擴展點

至于銷毀偶摔,是在容器關閉時調用的,詳見ConfigurableApplicationContext#close()

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末促脉,一起剝皮案震驚了整個濱河市辰斋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嘲叔,老刑警劉巖亡呵,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異硫戈,居然都是意外死亡锰什,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門丁逝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汁胆,“玉大人,你說我怎么就攤上這事霜幼∧勐耄” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵罪既,是天一觀的道長铸题。 經常有香客問我,道長琢感,這世上最難降的妖魔是什么丢间? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮驹针,結果婚禮上烘挫,老公的妹妹穿的比我還像新娘。我一直安慰自己柬甥,他們只是感情好饮六,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著苛蒲,像睡著了一般卤橄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上臂外,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天虽风,我揣著相機與錄音棒口,去河邊找鬼。 笑死辜膝,一個胖子當著我的面吹牛无牵,可吹牛的內容都是我干的。 我是一名探鬼主播厂抖,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼茎毁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了忱辅?” 一聲冷哼從身側響起七蜘,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎墙懂,沒想到半個月后橡卤,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡损搬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年碧库,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巧勤。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡嵌灰,死狀恐怖,靈堂內的尸體忽然破棺而出颅悉,到底是詐尸還是另有隱情沽瞭,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布剩瓶,位于F島的核電站驹溃,受9級特大地震影響,放射性物質發(fā)生泄漏延曙。R本人自食惡果不足惜吠架,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搂鲫。 院中可真熱鬧,春花似錦磺平、人聲如沸魂仍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽擦酌。三九已至,卻和暖如春菠劝,著一層夾襖步出監(jiān)牢的瞬間赊舶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留笼平,地道東北人园骆。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像寓调,于是被迫代替她去往敵國和親锌唾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345