Spring Bean的生命周期

Spring Bean的生命周期是Spring面試熱點問題。這個問題即考察對Spring的微觀了解疮茄,又考察對Spring的宏觀認識伦吠,想要答好并不容易!本文希望能夠從源碼角度入手惹骂,幫助面試者徹底搞定Spring Bean的生命周期苏携。

只有四個!

是的对粪,Spring Bean的生命周期只有這四個階段右冻。把這四個階段和每個階段對應的擴展點糅合在一起雖然沒有問題,但是這樣非常凌亂著拭,難以記憶国旷。要徹底搞清楚Spring的生命周期,首先要把這四個階段牢牢記住茫死。實例化和屬性賦值對應構造方法和setter方法的注入跪但,初始化和銷毀是用戶能自定義擴展的兩個階段。在這四步之間穿插的各種擴展點峦萎,稍后會講屡久。

????1.實例化 Instantiation

????2.屬性賦值 Populate

????3.初始化 Initialization

????4.銷毀 Destruction


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

主要邏輯都在doCreate()方法中,邏輯很清晰爱榔,就是順序調用以下三個方法被环,這三個方法與三個生命周期階段一一對應,非常重要详幽,在后續(xù)擴展接口分析中也會涉及筛欢。

1.createBeanInstance() -> 實例化

2.populateBean() -> 屬性賦值

3.initializeBean() -> 初始化

源碼如下浸锨,能證明實例化,屬性賦值和初始化這三個生命周期的存在版姑。關于本文的Spring源碼都將忽略無關部分柱搜,便于理解:

//?忽略了無關代碼

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);

???}

???}


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

常用擴展點

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

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

實現了這些接口的Bean會切入到多個Bean的生命周期中壤短。正因為如此设拟,這些接口的功能非常強大,Spring內部擴展也經常使用這些接口鸽扁,例如自動注入以及AOP的實現都和他們有關。

? ? 1.BeanPostProcessor

? ? 2.InstantiationAwareBeanPostProcessor

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


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

InstantiationAwareBeanPostProcessor?extends?BeanPostProcessor

InstantiationAwareBeanPostProcessor源碼分析:

@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;

????????}

????}

可以看到,postProcessBeforeInstantiation在doCreateBean之前調用赃承,也就是在bean實例化之前調用的妙黍,英文源碼注釋解釋道該方法的返回值會替換原本的Bean作為代理,這也是Aop等功能實現的關鍵點瞧剖。

postProcessAfterInstantiation調用點拭嫁,忽略無關代碼:

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ù)的屬性賦值操作代碼

}

可以看到該方法在屬性賦值方法內浇借,但是在真正執(zhí)行賦值操作之前。其返回值為boolean驮宴,返回false時可以阻斷屬性賦值階段(continueWithPropertyPopulation = false;)逮刨。

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

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

這一大類接口的特點是功能豐富修己,常用于用戶自定義擴展。

第二大類中又可以分為兩類:

? ? 1.Aware類型的接口

? ? 2.生命周期接口

無所不知的Aware

Aware類型的接口的作用就是讓我們能夠拿到Spring容器中的一些資源迎罗〔欠撸基本都能夠見名知意,Aware之前的名字就是可以拿到什么資源纹安,例如BeanNameAware可以拿到BeanName尤辱,以此類推。調用時機需要注意:所有的Aware方法都是在初始化階段之前調用的厢岂!

Aware接口眾多光督,這里同樣通過分類的方式幫助大家記憶。

Aware接口具體可以分為兩組塔粒,至于為什么這么分结借,詳見下面的源碼分析。如下排列順序同樣也是Aware接口的執(zhí)行順序卒茬,能夠見名知意的接口不再解釋船老。

Aware Group1

? ? 1.BeanNameAware

? ? 2.BeanClassLoaderAware

? ? 3.BeanFactoryAware

Aware Group2

? ? 1.EnvironmentAware

? ? 2.EmbeddedValueResolverAware 這個知道的人可能不多,實現該接口能夠獲取Spring EL解析器圃酵,用戶的自定義注解需要支持spel表達式的時候可以使用柳畔,非常方便。

? ? ????3.????ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware) 這幾個接口可能讓人有點懵郭赐,實際上這幾個接口可以一起記薪韩,其返回值實質上都是當前的ApplicationContext對象,因為ApplicationContext是一個復合接口捌锭,如下:

public?interface ApplicationContextextendsEnvironmentCapable,ListableBeanFactory,HierarchicalBeanFactory,

MessageSource,ApplicationEventPublisher,ResourcePatternResolver?{}

這里涉及到另一道面試題躬存,ApplicationContext和BeanFactory的區(qū)別,可以從ApplicationContext繼承的這幾個接口入手舀锨,除去BeanFactory相關的兩個接口就是ApplicationContext獨有的功能岭洲,這里不詳細說明。


Aware調用時機源碼分析

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

//?見名知意告私,初始化階段調用的方法

????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)實現的根悼。

????????wrappedBean?=?applyBeanPostProcessorsBeforeInitialization(wrappedBean,?beanName);

????????//?下文即將介紹的InitializingBean調用點

????????invokeInitMethods(beanName,?wrappedBean,?mbd);

????????//?BeanPostProcessor的另一個調用點

????????wrappedBean?=?applyBeanPostProcessorsAfterInitialization(wrappedBean,?beanName);

????????return?wrappedBean;

????}

可以看到并不是所有的Aware接口都使用同樣的方式調用。Bean××Aware都是在代碼中直接調用的蜀撑,而ApplicationContext相關的Aware都是通過BeanPostProcessor#postProcessBeforeInitialization()實現的挤巡。

感興趣的可以自己看一下ApplicationContextAwareProcessor這個類的源碼,就是判斷當前創(chuàng)建的Bean是否實現了相關的Aware方法酷麦,如果實現了會調用回調方法將資源傳遞給Bean矿卑。

至于Spring為什么這么實現,應該沒什么特殊的考量沃饶。也許和Spring的版本升級有關母廷。基于對修改關閉糊肤,對擴展開放的原則琴昆,Spring對一些新的Aware采用了擴展的方式添加。

BeanPostProcessor的調用時機也能在這里體現馆揉,包圍住invokeInitMethods方法业舍,也就說明了在初始化階段的前后執(zhí)行。

關于Aware接口的執(zhí)行順序把介,其實只需要記住第一組在第二組執(zhí)行之前就行了勤讽。每組中各個Aware方法的調用順序其實沒有必要記蟋座,有需要的時候點進源碼一看便知拗踢。

簡單的兩個生命周期接口

至于剩下的兩個生命周期接口就很簡單了,實例化和屬性賦值都是Spring幫助我們做的向臀,能夠自己實現的有初始化和銷毀兩個生命周期階段巢墅。

InitializingBean 對應生命周期的初始化階段,在上面源碼的invokeInitMethods(beanName, wrappedBean, mbd);方法中調用券膀。

有一點需要注意君纫,因為Aware方法都是執(zhí)行在初始化方法之前,所以可以在初始化方法中放心大膽的使用Aware接口獲取的資源芹彬,這也是我們自定義擴展Spring的常用方式蓄髓。

除了實現InitializingBean接口之外還能通過注解或者xml配置的方式指定初始化方法,至于這幾種定義方式的調用順序其實沒有必要記舒帮。因為這幾個方法對應的都是同一個生命周期会喝,只是實現方式不同陡叠,我們一般只采用其中一種方式。

DisposableBean 類似于InitializingBean肢执,對應生命周期的銷毀階段枉阵,以ConfigurableApplicationContext#close()方法作為入口,實現是通過循環(huán)取所有實現了DisposableBean接口的Bean然后調用其destroy()方法 预茄。感興趣的可以自行跟一下源碼兴溜。


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

注冊時機

我們知道BeanPostProcessor也會注冊為Bean,那么Spring是如何保證BeanPostProcessor在我們的業(yè)務Bean之前初始化完成呢耻陕?

請看我們熟悉的refresh()方法的源碼拙徽,省略部分無關代碼:

@Override

????publicvoidrefresh()throwsBeansException,?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();

????????????}

????}

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

執(zhí)行順序

BeanPostProcessor有很多個,而且每個BeanPostProcessor都影響多個Bean梧田,其執(zhí)行順序至關重要淳蔼,必須能夠控制其執(zhí)行順序才行。關于執(zhí)行順序這里需要引入兩個排序相關的接口:PriorityOrdered裁眯、Ordered

PriorityOrdered是一等公民鹉梨,首先被執(zhí)行,PriorityOrdered公民之間通過接口返回值排序穿稳,Ordered是二等公民存皂,然后執(zhí)行,Ordered公民之間通過接口返回值排序

都沒有實現是三等公民逢艘,最后執(zhí)行旦袋。

在以下源碼中,可以很清晰的看到Spring注冊各種類型BeanPostProcessor的邏輯它改,根據實現不同排序接口進行分組疤孕。優(yōu)先級高的先加入,優(yōu)先級低的后加入央拖。

//?First,?invoke?the?BeanDefinitionRegistryPostProcessors?that?implement?PriorityOrdered.

//?首先祭阀,加入實現了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.

//?然后鲜戒,加入實現了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();

????????????}

根據排序接口返回值排序,默認升序排序遏餐,返回值越低優(yōu)先級越高伦腐。

????/**

?????*?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;

PriorityOrdered、Ordered接口作為Spring整個框架通用的排序接口失都,在Spring中應用廣泛柏蘑,也是非常重要的接口颖系。

總結

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

四個階段

實例化 Instantiation

屬性賦值 Populate

初始化 Initialization

銷毀 Destruction

多個擴展點

影響多個Bean

BeanPostProcessor

InstantiationAwareBeanPostProcessor

影響單個Bean

Aware

Aware Group1

BeanNameAware

BeanClassLoaderAware

BeanFactoryAware

Aware Group2

EnvironmentAware

EmbeddedValueResolverAware

ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware)

生命周期

InitializingBean

DisposableBean

至此嘁扼,Spring Bean的生命周期介紹完畢,由于作者水平有限難免有疏漏黔攒,歡迎留言糾錯趁啸。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市督惰,隨后出現的幾起案子不傅,更是在濱河造成了極大的恐慌,老刑警劉巖赏胚,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件访娶,死亡現場離奇詭異,居然都是意外死亡觉阅,警方通過查閱死者的電腦和手機崖疤,發(fā)現死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來典勇,“玉大人劫哼,你說我怎么就攤上這事「铙希” “怎么了权烧?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長伤溉。 經常有香客問我般码,道長,這世上最難降的妖魔是什么乱顾? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任板祝,我火速辦了婚禮,結果婚禮上糯耍,老公的妹妹穿的比我還像新娘扔字。我一直安慰自己囊嘉,他們只是感情好温技,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扭粱,像睡著了一般舵鳞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上琢蛤,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天蜓堕,我揣著相機與錄音抛虏,去河邊找鬼。 笑死套才,一個胖子當著我的面吹牛迂猴,可吹牛的內容都是我干的。 我是一名探鬼主播背伴,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼沸毁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了傻寂?” 一聲冷哼從身側響起息尺,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疾掰,沒想到半個月后搂誉,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡静檬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年炭懊,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拂檩。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡凛虽,死狀恐怖,靈堂內的尸體忽然破棺而出广恢,到底是詐尸還是另有隱情凯旋,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布钉迷,位于F島的核電站至非,受9級特大地震影響,放射性物質發(fā)生泄漏糠聪。R本人自食惡果不足惜荒椭,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舰蟆。 院中可真熱鬧趣惠,春花似錦、人聲如沸身害。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽塌鸯。三九已至侍瑟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涨颜。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工费韭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人庭瑰。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓星持,卻偏偏與公主長得像,于是被迫代替她去往敵國和親弹灭。 傳聞我的和親對象是個殘疾皇子钉汗,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內容