Tomcat之啟動(dòng)過(guò)程源碼分析

LifecycleBase生命周期抽象類

在分析啟動(dòng)流程之前氯哮,有必要對(duì)于實(shí)現(xiàn)了LifecycleBase這個(gè)類的所有類進(jìn)行分析。結(jié)論是只要調(diào)用生命周期方法食铐,都會(huì)先調(diào)用這個(gè)LifecycleBase的父類方法遭京。以StandardServer和StandardEngine容器類為例

1、StandardServer

在具體分析之前需要看下StandardServer的繼承關(guān)系圖


image.png

StandardServer繼承了生命周期抽象類LifecycleMBeanBase柳畔,LifecycleMBeanBase繼承了LifecycleBase抽象類。由于StandardServer沒(méi)有init辜昵、start荸镊、stop咽斧、destroy等方法堪置,所以會(huì)到父類尋找這些方法。


image.png

①张惹、init
org.apache.catalina.util.LifecycleBase#init
image.png

調(diào)用setStateInternal方法
發(fā)布LifecycleState.INITIALIZING是before_init的生命周期事件
②舀锨、setStateInternal
org.apache.catalina.util.LifecycleBase#setStateInternal


image.png

image.png

image.png

這個(gè)方法最重要的實(shí)現(xiàn)邏輯就是發(fā)布當(dāng)前事件
③、fireLifecycleEvent
org.apache.catalina.util.LifecycleBase#fireLifecycleEvent
image.png

根據(jù)傳入類型type = "before_init"獲取當(dāng)前事件宛逗,然后調(diào)用當(dāng)前容器包含的LifecycleListener的lifecycleEvent方法坎匿。
④、initInternal

initInternal方法會(huì)調(diào)用到當(dāng)前容器的initInternal方法。


image.png
2替蔬、StandardEngine繼承圖

繼承了ContainerBase告私,ContainerBase繼承了LifecycleMBeanBase并實(shí)現(xiàn)了Container。
LifecycleMBeanBase繼承了LifecycleBase抽象類承桥。


image.png

①驻粟、start
org.apache.catalina.util.LifecycleBase#start


image.png

發(fā)布LifecycleState.STARTING_PREP是before_start事件
image.png

②、startInternal

然后調(diào)用startInternal方法


image.png
Lifecycle有哪些LifecycleState事件

before_init凶异、after_init蜀撑、start、before_start剩彬、after_start酷麦、stop、before_stop喉恋、after_stop沃饶、after_destroy、before_destroy瀑晒、periodic绍坝、configure_start、configure_stop
所以苔悦,任何容器類StandardService轩褐、StandardEngine、StandardHost玖详、StandardContext把介、StandardWrapper及其他實(shí)現(xiàn)了LifecycleBase調(diào)用生命周期方法都是這樣的調(diào)用流程

Bootstrap啟動(dòng)Tomcat

Tomcat啟動(dòng)通過(guò)Bootstrap類main方法啟動(dòng)


image.png

org.apache.catalina.startup.Bootstrap#main


image.png

image.png

main方法主要步驟有三步:

①、init()
②蟋座、load()
③拗踢、start()

1、init

org.apache.catalina.startup.Bootstrap#init()


image.png

①向臀、初始化類加載器
org.apache.catalina.startup.Bootstrap#initClassLoaders


image.png

commonLoader巢墅、catalinaLoader、sharedLoader類加載器都是URLClassLoader
當(dāng)前線程設(shè)置類加載器catalinaLoader
image.png

②券膀、啟動(dòng)類加載并創(chuàng)建實(shí)例對(duì)象Catalina
設(shè)置Catalina類的父加載器setParentClassLoader是URLClassLoader


image.png

setAwait

catalina設(shè)置Await是阻塞君纫,true表示阻塞


image.png

2、load

org.apache.catalina.startup.Bootstrap#load


image.png

org.apache.catalina.startup.Catalina#load()


image.png

①芹彬、創(chuàng)建Digester

獲取file文件conf\server.xml蓄髓,創(chuàng)建Digester對(duì)象。這個(gè)類的目的就是把server.xml中的層級(jí)結(jié)構(gòu)和生命周期實(shí)現(xiàn)類都創(chuàng)建出來(lái)舒帮,包括容器類和監(jiān)聽(tīng)器類会喝。


image.png

org.apache.catalina.startup.Catalina#createStartDigester
digester對(duì)象是解析server.xml文件


image.png

fakeAttributes通過(guò)鍵值對(duì)添加
image.png

digester的規(guī)則添加到Rules對(duì)象


image.png

digester解析文件reader = SAXParserImpl$JAXPSAXParser
image.png

image.png

image.png

②陡叠、server.init初始化

設(shè)置server的Catalina屬性


image.png

開(kāi)始server的init
容器對(duì)象都實(shí)現(xiàn)了LifecycleBase聲明周期方法,所以容器對(duì)象都會(huì)調(diào)用到org.apache.catalina.util.LifecycleBase#init肢执,然后調(diào)用到容器的initInternal方法


image.png

org.apache.catalina.core.StandardServer#initInternal
image.png
③枉阵、services.init()

org.apache.catalina.util.LifecycleBase#init


image.png

org.apache.catalina.core.StandardService#initInternal


image.png

image.png
④、engine.init()

org.apache.catalina.core.StandardEngine#initInternal


image.png

org.apache.catalina.core.StandardEngine#getRealm


image.png

org.apache.catalina.core.ContainerBase#initInternal
image.png

org.apache.catalina.core.ContainerBase#reconfigureStartStopExecutor
創(chuàng)建內(nèi)嵌的startStopExecutor執(zhí)行器预茄,用來(lái)處理部分容器類


image.png
⑤岭妖、executor.init()和mapperListener..init()

如果有自定義executors將會(huì)進(jìn)行初始化


image.png
⑥、connector.init()

初始化連接
初始化adapter
org.apache.catalina.connector.Connector#initInternal


image.png

protocolHandler = Http11NioProtocol驗(yàn)證是否有效


image.png

image.png

org.apache.coyote.http11.AbstractHttp11Protocol#init
image.png

org.apache.coyote.AbstractProtocol#init
image.png

org.apache.tomcat.util.net.AbstractEndpoint#init


image.png

org.apache.tomcat.util.net.AbstractEndpoint#bindWithCleanup
image.png

org.apache.tomcat.util.net.NioEndpoint#bind
image.png

org.apache.tomcat.util.net.NioEndpoint#initServerSocket
創(chuàng)建serverSock對(duì)象反璃,綁定端口昵慌,設(shè)置為非阻塞。服務(wù)端sockets啟動(dòng)
image.png

到這里便開(kāi)啟服務(wù)端Socket等待客戶端連接

3淮蜈、start

org.apache.catalina.startup.Bootstrap#start


image.png
3.1斋攀、StandardServer啟動(dòng)start

org.apache.catalina.startup.Catalina#start


image.png

這里也會(huì)調(diào)用到父類LifecycleBase#start方法
org.apache.catalina.util.LifecycleBase#start


image.png

org.apache.catalina.core.StandardServer#startInternal
3.2、StandardService啟動(dòng)start

開(kāi)啟services


image.png

開(kāi)啟一個(gè)startPeriodicLifecycleEvent梧田,用來(lái)后臺(tái)執(zhí)行進(jìn)行熱加載和熱部署


image.png

此時(shí)處于StandardService階段淳蔼,這里的lifecycleListeners 數(shù)量是0,所以setState方法什么都沒(méi)做
image.png
3.3裁眯、StandardEngine啟動(dòng)start

開(kāi)啟engine


image.png

開(kāi)啟engine后


image.png

主要步驟是:executor鹉梨、mapperListener、connector的開(kāi)啟
org.apache.catalina.core.StandardEngine#startInternal

在StandardEngine階段穿稳,lifecycleListeners有EngineConfig監(jiān)聽(tīng)器


image.png

org.apache.catalina.core.ContainerBase#startInternal
image.png

步驟包括:cluster存皂、realm的start調(diào)用、獲取孩子容器StandardHost
主要分析獲取的孩子容器StandardHost
把得到的孩子容器封裝成StartChild任務(wù)類逢艘,使用startStopExecutor線程池開(kāi)啟任務(wù)
image.png
3.3.1旦袋、executor啟動(dòng)start

executors線程池為null這里沒(méi)有做什么

3.3.2、mapperListener啟動(dòng)start
image.png

org.apache.catalina.mapper.MapperListener#startInternal


image.png

org.apache.catalina.mapper.MapperListener#addListeners
listeners和lifecycleListeners添加MapperListener監(jiān)聽(tīng)器它改,然后遞歸調(diào)用為孩子容器都添加這個(gè)MapperListener監(jiān)聽(tīng)器


image.png

image.png

org.apache.catalina.mapper.MapperListener#registerHost
image.png

org.apache.catalina.mapper.MapperListener#registerContext


image.png

image.png

org.apache.catalina.mapper.MapperListener#prepareWrapperMappingInfo
對(duì)每個(gè)wrapper尋找對(duì)應(yīng)的映射關(guān)系并添加到wrappers中
image.png

image.png

image.png
3.3.3疤孕、connector啟動(dòng)start

org.apache.catalina.core.StandardService#startInternal


image.png

org.apache.catalina.connector.Connector#startInternal


image.png

org.apache.coyote.AbstractProtocol#start
image.png

org.apache.tomcat.util.net.AbstractEndpoint#start


image.png

org.apache.tomcat.util.net.NioEndpoint#startInternal
image.png

創(chuàng)建executor工作線程池
image.png

poller線程啟動(dòng)
acceptor線程啟動(dòng)
image.png
3.4、StandardHost啟動(dòng)start

org.apache.catalina.core.ContainerBase.StartChild#call


image.png

lifecycleListeners包含HostConfig央拖,所以在執(zhí)行start之前
org.apache.catalina.util.LifecycleBase#start


image.png

org.apache.catalina.util.LifecycleBase#setStateInternal
發(fā)布一個(gè)lifecycleEvent = "before_start"事件
image.png

org.apache.catalina.util.LifecycleBase#fireLifecycleEvent
HostConfig監(jiān)聽(tīng)器處理before_start事件


image.png

org.apache.catalina.startup.HostConfig#lifecycleEvent
image.png

check()方法是periodic事件祭阀,用來(lái)熱部署。beforeStart就是before_start事件
org.apache.catalina.startup.HostConfig#beforeStart
image.png

得到webapps和conf/Catalina/loaclhost文件夾
org.apache.catalina.core.StandardHost#startInternal
image.png

pipeline添加閾值通過(guò)addValve
image.png

調(diào)用父類方法
StandardHost[localhost]尋找孩子容器鲜戒,與StandardEngine過(guò)程相同


image.png

封裝成StartChild交給startStopExecutor線程池處理
3.5专控、StandardContext啟動(dòng)start
image.png

此時(shí)lifecycleListeners包括(CopyOnWriteArrayList 3個(gè)):ContextConfig、StandardHost$MemoryLeakTrackingListener 袍啡、ThreadLocalLeakPreventionListener
org.apache.catalina.util.LifecycleBase#setStateInternal
發(fā)布LifecycleState.STARTING_PREP事件是before_start


image.png

org.apache.catalina.util.LifecycleBase#fireLifecycleEvent


image.png

對(duì)三個(gè)監(jiān)聽(tīng)器LifecycleListener調(diào)用lifecycleEvent
org.apache.catalina.startup.ContextConfig#lifecycleEvent
image.png

image.png

org.apache.catalina.startup.ContextConfig#fixDocBase

調(diào)整docBase


image.png

org.apache.catalina.startup.ContextConfig#antiLocking
對(duì)antiResourceLocking是true的處理踩官,默認(rèn)是false
image.png

另外兩個(gè)LifecycleListener什么都沒(méi)做
StandardContext.startInternal()

org.apache.catalina.core.StandardContext#startInternal
①却桶、設(shè)置resources


image.png

org.apache.catalina.core.StandardContext#setResources


image.png

image.png

把傳入的resources賦值給this(StandardContext)的resources
image.png

②境输、resources的啟動(dòng)
org.apache.catalina.core.StandardContext#resourcesStart


image.png

StandardRoot實(shí)現(xiàn)了LifecycleBase蔗牡,所以直接到startInternal方法
org.apache.catalina.webresources.StandardRoot#startInternal
image.png

org.apache.catalina.webresources.StandardRoot#createMainResourceSet
創(chuàng)建包含當(dāng)前Context的路徑的WebResourceSet并返回
image.png

image.png

org.apache.catalina.webresources.StandardRoot#processWebInfLib
image.png

jar包資源resources也添加到StandradRoot的allResources中


image.png

image.png

③、創(chuàng)建webappLoader類嗅剖,并設(shè)置到當(dāng)前Context的loader中
image.png

image.png

④辩越、當(dāng)前應(yīng)用加載器loader,啟動(dòng)start
image.png

WebappLoader實(shí)現(xiàn)了LifecycleBase
org.apache.catalina.loader.WebappLoader#startInternal
image.png

org.apache.catalina.loader.WebappLoader#createClassLoader
根據(jù)ParallelWebappClassLoader類的構(gòu)造方法信粮,傳入父類加載器參是URLClassLoader黔攒。創(chuàng)建一個(gè)WebappClassLoaderBase對(duì)象。傳入的resources是StandardRoot對(duì)象
image.png

image.png

org.apache.catalina.loader.WebappClassLoaderBase#start
把resources中的/WEB-INF/classes和/WEB-INF/lib目錄添加到localRepositories中
image.png

image.png

⑤强缘、解析web.xml文件
image.png

image.png

org.apache.catalina.startup.ContextConfig#lifecycleEvent
image.png

image.png

org.apache.catalina.startup.ContextConfig#webConfig


image.png

image.png

org.apache.tomcat.util.descriptor.web.WebXmlParser#parseWebXml(org.xml.sax.InputSource, org.apache.tomcat.util.descriptor.web.WebXml, boolean)
image.png

image.png

org.apache.tomcat.util.digester.Digester#parse(org.xml.sax.InputSource)
image.png

reader = SAXParserImpl$JAXPSAXParser
image.png

解析到servlets督惰、servletMappings、 filters旅掂、 filterMaps等屬性中
image.png

⑥赏胚、尋找ServletContainerInitializer實(shí)現(xiàn)
image.png

org.apache.catalina.startup.ContextConfig#processServletContainerInitializers
加載后的實(shí)現(xiàn)放入到detectedScis集合中
image.png

得到HandlesTypes注解
image.png

image.png

⑦、processClasses商虐、合并web-fragment.xml觉阅、合并tomcat-web.xml
image.png

⑧、JSP處理秘车、把web.xml配置到Context
org.apache.catalina.startup.ContextConfig#configureContext
配置filter典勇、filterMap、listener
image.png

SecurityConstraint叮趴、role割笙、ContextService
image.png

servlet設(shè)置
image.png

ServletMapping、sessionConfig
image.png

配置PostConstruct眯亦、PreDestroy方法
image.png

ServletContainerInitializer實(shí)現(xiàn)添加到context中
image.png

⑨咳蔚、回調(diào)ServletContainerInitializers的onStartup方法
image.png
3.6、部署應(yīng)用

發(fā)布LifecycleState.STARTING是start的事件
org.apache.catalina.core.ContainerBase#startInternal
這里是StandardHost容器階段搔驼。時(shí)機(jī)是在開(kāi)始Host谈火,把Host的孩子容器節(jié)點(diǎn)封裝到StartChild任務(wù)類,在線程池startStopExecutor調(diào)用的舌涨,然后調(diào)用results的get回調(diào)方法糯耍,pipeline開(kāi)始方法之后調(diào)用的


image.png

image.png

org.apache.catalina.startup.HostConfig#lifecycleEvent


image.png

設(shè)置copyXML(false)、deployXML(true)囊嘉、unpackWARs(true)温技、contextClass(org.apache.catalina.core.StandardContext)的參數(shù)值
image.png

org.apache.catalina.startup.HostConfig#start
image.png
deployApps()部署應(yīng)用

org.apache.catalina.startup.HostConfig#deployApps()


image.png

①、deployDescriptors
org.apache.catalina.startup.HostConfig#deployDescriptors
處理部署在conf/Catalina/localhost/文件夾下XML形式的context


image.png

如果沒(méi)有部署過(guò)則封裝成DeployDescriptor任務(wù)類放到InlineExecutorService執(zhí)行
②扭粱、deployWARs

org.apache.catalina.startup.HostConfig#deployWARs
部署war文件舵鳞,webapps目錄下以.war結(jié)尾的


image.png

如果沒(méi)有部署過(guò)則封裝成DeployWar任務(wù)類放到InlineExecutorService執(zhí)行
image.png

③、deployDirectories
處理webapps目錄下的文件夾
org.apache.catalina.startup.HostConfig#deployDirectories
image.png

如果沒(méi)有部署過(guò)則封裝成DeployDirectory任務(wù)類放到InlineExecutorService執(zhí)行

總結(jié):

Tomcat的啟動(dòng)時(shí)通過(guò)啟動(dòng)類的main方法琢蛤,主要有三步:初始化(init)蜓堕、加載(load)抛虏、啟動(dòng)(start)
1、初始化類加載器和Catalina類
2套才、根據(jù)server.xml創(chuàng)建Digester迂猴,所定義的節(jié)點(diǎn)都會(huì)生成對(duì)應(yīng)的對(duì)象,節(jié)點(diǎn)所處的層級(jí)也會(huì)解析出來(lái)背伴,設(shè)置不同容器的pipeline基礎(chǔ)的Value閾值沸毁。server、services傻寂、engine息尺、executor、mapperListener疾掰、connector的初始化調(diào)用掷倔,其中最重要的是endpoint的serverSocket的創(chuàng)建
3、StandardServer个绍、StandardService和StandardEngine勒葱、StandardHost、StandardContext等容器的啟動(dòng)巴柿。
①凛虽、由于都實(shí)現(xiàn)了生命周期抽象類,所以會(huì)先調(diào)用LifecycleBase方法广恢。在LifecycleBase方法內(nèi)會(huì)使用調(diào)用setStateInternal發(fā)布一個(gè)事件凯旋,根據(jù)當(dāng)前所處的容器階段會(huì)調(diào)用相對(duì)應(yīng)的監(jiān)聽(tīng)器實(shí)現(xiàn)類。之后再調(diào)用所處容器的生命周期方法钉迷。
②至非、StandardHost和StandardContext都是在父類容器查找孩子容器然后封裝成一個(gè)任務(wù)類放入到線程池中執(zhí)行啟動(dòng)的。
③糠聪、在容器啟動(dòng)中荒椭,其中StandardContext的啟動(dòng)是最核心一個(gè)也是復(fù)雜的一個(gè)。主要包括WebappLoader類加載器創(chuàng)建和啟動(dòng)舰蟆、解析web.xml文件(把解析后的值放到WebXML對(duì)象)趣惠、ServletContainerInitializers的查找及啟動(dòng)、把WebXML對(duì)象封裝到當(dāng)前Context對(duì)象等過(guò)程
④身害、容器啟動(dòng)后味悄,就要部署應(yīng)用。有三種部署形式塌鸯,也都是封裝成任務(wù)類放入到線程池內(nèi)執(zhí)行侍瑟。部署應(yīng)用分為部署server.xml中定義的Context和部署webapp文件夾下的Context

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者丙猬。
  • 序言:七十年代末涨颜,一起剝皮案震驚了整個(gè)濱河市费韭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌咐低,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袜腥,死亡現(xiàn)場(chǎng)離奇詭異见擦,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)羹令,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)鲤屡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人福侈,你說(shuō)我怎么就攤上這事酒来。” “怎么了肪凛?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵堰汉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我伟墙,道長(zhǎng)翘鸭,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任戳葵,我火速辦了婚禮就乓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拱烁。我一直安慰自己生蚁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布戏自。 她就那樣靜靜地躺著邦投,像睡著了一般。 火紅的嫁衣襯著肌膚如雪擅笔。 梳的紋絲不亂的頭發(fā)上尼摹,一...
    開(kāi)封第一講書(shū)人閱讀 51,604評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音剂娄,去河邊找鬼蠢涝。 笑死,一個(gè)胖子當(dāng)著我的面吹牛阅懦,可吹牛的內(nèi)容都是我干的和二。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼耳胎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼惯吕!你這毒婦竟也來(lái)了惕它?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤废登,失蹤者是張志新(化名)和其女友劉穎淹魄,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體堡距,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡甲锡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了羽戒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缤沦。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖易稠,靈堂內(nèi)的尸體忽然破棺而出缸废,到底是詐尸還是另有隱情,我是刑警寧澤驶社,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布企量,位于F島的核電站,受9級(jí)特大地震影響亡电,放射性物質(zhì)發(fā)生泄漏梁钾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一逊抡、第九天 我趴在偏房一處隱蔽的房頂上張望姆泻。 院中可真熱鬧,春花似錦冒嫡、人聲如沸拇勃。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)方咆。三九已至,卻和暖如春蟀架,著一層夾襖步出監(jiān)牢的瞬間瓣赂,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工片拍, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留煌集,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓捌省,卻偏偏與公主長(zhǎng)得像苫纤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355