[譯]Spring Bean的生命周期

原文:Spring Bean Lifecycle


Spring IoC(Inversion of Control雹拄,控制反轉(zhuǎn))容器管理著Spring中的bean森篷。一個(gè)"Spring bean"只是一個(gè)由Spring管理的Java類的實(shí)例桃煎。
Spring的IoC容器負(fù)責(zé)實(shí)例化、初始化并寫入bean辑舷,同時(shí)也管理這些bean的生命周期砾莱。
Spring提供了一些方法,你可以通過它們來進(jìn)入bean的生命周期胰丁。 例如随橘,一旦一個(gè)bean被實(shí)例化,你可能就需要執(zhí)行一些初始化操作來使這個(gè)bean進(jìn)入可用狀態(tài)锦庸。類似地机蔗,你可能需要在一個(gè)bean被從容器移除之前進(jìn)行資源的清理。
在這篇文章中,我們會(huì)研究Spring bean生命周期的各個(gè)階段萝嘁。這就是Spring框架創(chuàng)建和銷毀Spring bean的方式梆掸。

Spring Bean 生命周期概覽

下面的圖片展示了Spring bean生命周期的兩個(gè)部分:

image

Part 1:展示了一個(gè)bean在實(shí)例化之后到準(zhǔn)備就緒可用前所經(jīng)歷的不同階段。
Part 2:展示了當(dāng)一個(gè)Spring Ioc容器關(guān)閉牙言,一個(gè)bean會(huì)經(jīng)歷些什么酸钦。
正如你在前面圖片的Part 1中所看見的,容器通過調(diào)用它的構(gòu)造器來實(shí)例化一個(gè)bean然后填充它的屬性咱枉。
之后對(duì)bean進(jìn)行多次調(diào)用卑硫,直到bean處于就緒狀態(tài)。
類似地蚕断,像Part 2中展示的那樣欢伏,當(dāng)容器關(guān)閉,容器調(diào)用bean來使其能夠在銷毀bean之前執(zhí)行所有需要的任務(wù)亿乳。

Aware接口

Spring提供了一些aware接口硝拧,他們用來進(jìn)入Spring框架的基礎(chǔ)層。aware接口主要在框架中使用葛假,很少被Spring程序員使用障陶。
作為一個(gè)Spring程序員應(yīng)該熟悉下面三個(gè)aware接口:

  • BeanFactoryAware:提供setBeanFactory(),一個(gè)提供bean實(shí)例所屬工廠的回調(diào)聊训。
  • BeanNameAware: 此接口的ThesetBeanName()回調(diào)提供了bean的名稱咸这。
  • ApplicationContextAware:此接口的ThesetApplicationContext()方法提供了此bean的ApplicationContext對(duì)象
    使用上述aware接口的代碼如下:
package guru.springframework.springbeanlifecycle.awareinterfaces.domain;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.Arrays;
public class AwareBeanImpl implements ApplicationContextAware, BeanNameAware, BeanFactoryAware {
  @Override
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    System.out.println("setBeanFactory method of AwareBeanImpl is called");
    System.out.println("setBeanFactory:: AwareBeanImpl singleton= "
    + beanFactory.isSingleton("awareBean"));
  }
  @Override
  public void setBeanName(String beanName) {
    System.out.println("setBeanName method of AwareBeanImpl is called");
    System.out.println("setBeanName:: Bean Name defined in context= "
    + beanName);
  }
  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    System.out.println("setApplicationContext method of AwareBeanImpl is called");
    System.out.println("setApplicationContext:: Bean Definition Names= "
    + Arrays.toString(applicationContext.getBeanDefinitionNames()));
  }
}

前面的bean實(shí)現(xiàn)了ApplicationContextAwareBeanNameAwareBeanFactoryAware接口魔眨。
代碼行13-18:代碼重寫了BeanFactoryAware接口的setBeanFactory()方法。在運(yùn)行期間酿雪,Spring傳遞創(chuàng)建bean的BeanFactory對(duì)象遏暴。該代碼使用這個(gè)BeanFactory對(duì)象來打印這個(gè)bean是否是一個(gè)單例。
代碼行20-25:重寫了BeanNameAware接口的setBeanName()方法指黎。在運(yùn)行期間朋凉,Spring以一個(gè)String來傳遞這個(gè)bean的名字,被該代碼打印了出來醋安。代碼使用了beanName來打印上下文中定義的bean的名字杂彭。
代碼行27-32:重寫了ApplicationContextAware接口的setApplicationContext()方法,在運(yùn)行期間吓揪,Spring傳遞創(chuàng)建了這個(gè)bean的ApplicationContext對(duì)象亲怠。代碼用這個(gè)ApplicationContext對(duì)象來打印bean定義的名字。
接下來柠辞,我們將編寫bean的配置來定義AwareBeanImpl团秽。beans.xml的代碼如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   <!--    awareinterfaces-->
   <bean id="awareBean" class="guru.springframework.springbeanlifecycle.awareinterfaces.domain.AwareBeanImpl">
   </bean>
</beans>

最后,讓我們編寫主類,它將加載bean .xml并測(cè)試相關(guān)接口方法:

package guru.springframework.springbeanlifecycle;
import guru.springframework.springbeanlifecycle.awareinterfaces.domain.AwareBeanImpl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SpringBootApplication
public class SpringBeanLifecycleApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringBeanLifecycleApplication.class, args);
    // -------awareinterfaces---------
    ApplicationContext context1 =
    new ClassPathXmlApplicationContext("beans.xml");
    AwareBeanImpl awareBeanImpl = (AwareBeanImpl) context1.getBean("awareBean");
    ((AbstractApplicationContext) context1).registerShutdownHook();
  }
}

運(yùn)行主類的輸入如下:

setBeanName method of AwareBeanImpl is called
setBeanName:: Bean Name defined in context= awareBean
setBeanFactory method of AwareBeanImpl is called
setBeanFactory:: AwareBeanImpl singleton= true
setApplicationContext method of AwareBeanImpl is called
setApplicationContext:: Bean Definition Names= [awareBean]

Bean后置處理

Spring提供了BeanPostProcessor接口习勤,為你提供了進(jìn)入Spring上下文生命周期和在處理bean時(shí)和他們交互的方法踪栋。
BeanPostProcessor接口包含兩個(gè)方法:

  • postProcessBeforeInitialization:Spring在調(diào)用aware接口的方法之后和任何bean的初始化回調(diào)之前調(diào)用此方法,例如InitializingBeanafterPropertiesSet()或者任何自定義的初始化方法图毕。
  • postProcessAfterInitialization:Spring在任何bean的初始化回調(diào)之后調(diào)用此方法夷都。

讓我們從創(chuàng)建一個(gè)叫BookName的bean開始:

package guru.springframework.springbeanlifecycle.beanpostprocessor.domain;
public class BookBean {
    private String bookName;
    public BookBean() {
        System.out.println("Constructor of BookBean called !! ");
    }
    public BookBean(String bookName) {
        this.bookName = bookName;
    }
    public String getBookName() {
        return bookName;
    }
    public void setBookName(String bookName) {
        this.bookName = bookName;
    }
    @Override
    public String toString() {
        return "BookBean{" +
        "bookName='" + bookName + '\'' +
        '}';
    }
}

接下來,我們創(chuàng)建一個(gè)BookBeanPostProcessor予颤。
BookBeanPostProcessor代碼如下:

package guru.springframework.springbeanlifecycle.beanpostprocessor.domain;
import guru.springframework.springbeanlifecycle.beanpostprocessor.domain.BookBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class BookBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Post Process Before Initialization method is called : Bean Name " + beanName);
        return bean; 
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Post Process After Initialization method is called : Bean Name " + beanName);
        return bean;
    }
}

上述代碼實(shí)現(xiàn)了BeanPostProcessor接口并覆蓋了postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法囤官。
Spring在調(diào)用了aware接口的方法之后。調(diào)用postProcessBeforeInitialization()方法荣瑟。
Spring在所有的bean初始化回調(diào)之后調(diào)用postProcessAfterInitialization()方法治拿,例如InitializingBean的afterPropertiesSet()或者任何自定義的初始化方法。我們接下來兩個(gè)都會(huì)討論笆焰。
在運(yùn)行時(shí)劫谅,Spring將向這兩個(gè)方法注入新的bean實(shí)例和bean名稱。
接下來嚷掠,我們會(huì)在XML配置中定義BookBeanBookBeanProcessor作為bean捏检。
配置代碼如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="bookBeanPost" class="guru.springframework.springbeanlifecycle.beanpostprocessor.domain.BookBean">
<property name="bookName" value="Gone with the Wind"></property>
</bean>
<bean id="bookBeanPostProcessor"
class="guru.springframework.springbeanlifecycle.beanpostprocessor.domain.BookBeanPostProcessor"/>
</beans>

測(cè)試我們的BeanPostProcessor的代碼如下:

package guru.springframework.springbeanlifecycle;
import guru.springframework.springbeanlifecycle.beanpostprocessor.domain.BookBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SpringBootApplication
public class SpringBeanLifecycleApplication {
public static void main(String[] args) {
    SpringApplication.run(SpringBeanLifecycleApplication.class, args);
    // -------beanpostprocessor------
    ApplicationContext context4 =
    new ClassPathXmlApplicationContext("beans.xml");
    BookBean bookBean = (BookBean) context4.getBean("bookBeanPost");
    ((AbstractApplicationContext) context4).registerShutdownHook();
    }
}

輸出如下:

  • Constructor of BookBean called !!
  • Post Process After Initialization method is called: Bean Name bookBeanPost
  • Post Process Before Initialization method is called: Bean Name bookBeanPost

InitializingBean和DisposableBean回調(diào)接口

Spring提供下列兩個(gè)回調(diào)接口:

  • InitializingBean:聲明了afterPropertiesSet()方法,能夠用來編寫初始化邏輯不皆。容器在屬性設(shè)置完成后調(diào)用這個(gè)方法贯城。
  • DisposableBean:聲明了destroy()方法,能夠用來編寫清理代碼霹娄。容器在關(guān)閉的bean銷毀期間調(diào)用此方法
    讓我們來編寫一個(gè)實(shí)現(xiàn)了InitializingBean和DisposableBean接口的bean能犯。
    bean Book的代碼如下:
package guru.springframework.springbeanlifecycle.callbackinterfaces.domain;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class Book implements InitializingBean, DisposableBean {
    private String bookName;
    public Book() {
        System.out.println("Constructor of Book bean is called !! ");
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("Destroy method of Book bean called !! ");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet method of Book bean is called !! ");
    }
    public Book(String bookName) {
        this.bookName = bookName;
    }
    public String getBookName() {
        eturn bookName;
    }
    public void setBookName(String bookName) {
        this.bookName = bookName;
    }
    @Override
    public String toString() {
        return "Book{" +
        "bookName='" + bookName + '\'' +
        '}';
    }
}

前面的Book bean實(shí)現(xiàn)了InitializingBeanDisposableBean接口,并覆蓋了它們的afterPropertiesSet()和destroy()方法犬耻。
接下來踩晶,我們要編寫bean配置來定義Book的bean。
beans.xml的代碼如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- callbackinterfaces-->
<bean id="bookBean" class="guru.springframework.springbeanlifecycle.callbackinterfaces.domain.Book">
<property name="bookName" value="Believe in Yourself"/>
</bean>
</beans>

main類如下

package guru.springframework.springbeanlifecycle;
import guru.springframework.springbeanlifecycle.callbackinterfaces.domain.Book;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SpringBootApplication
public class SpringBeanLifecycleApplication {
public static void main(String[] args) {
    SpringApplication.run(SpringBeanLifecycleApplication.class, args);
    // -------callbackinterfaces-------
    ApplicationContext context =
    new ClassPathXmlApplicationContext("beans.xml");
    Book book = (Book) context.getBean("bookBean");
    System.out.println(book.getBookName());
    ((AbstractApplicationContext) context).registerShutdownHook();
    }
}

前面的代碼從ApplicationContext中取出了Book的bean枕磁,并打印出了bookName屬性渡蜻。
運(yùn)行主類的輸出如下:

  • The constructor of Book bean is called !!
  • afterPropertiesSet method of Book bean is called !!
  • Believe in Yourself
  • destroy method of Book bean is called !!

正如您在輸出中所注意到的,afterPropertiesSet()方法首先被調(diào)用计济。

自定義初始化和銷毀方法

當(dāng)在XML配置中聲明bean時(shí)茸苇,你可以在tag中指定init-method和destroy-method屬性。這兩個(gè)屬性都指定了bean類中的自定義方法沦寂。
init-method屬性中聲明的方法是在Spring通過setter或構(gòu)造函數(shù)參數(shù)初始化bean屬性之后調(diào)用的学密。您可以使用此方法來驗(yàn)證注入的屬性或執(zhí)行任何其他任務(wù)。
Spring在銷毀bean之前調(diào)用destroy-method屬性中聲明的方法传藏。
讓我們使用一個(gè)名為BookCustomBean的bean中的自定義init和destroy方法则果。
BookCustomBean的代碼如下:

package guru.springframework.springbeanlifecycle.custominitanddestroy.domain;
public class BookCustomBean {
    private String bookName;
    public BookCustomBean() {
        System.out.println("Constructor of BookCustomBean bean is called !! ");
    }
    public void customDestroy() throws Exception {
        System.out.println("Custom destroy method of BookCustomBean called !! ");
    }
    public void customInit() throws Exception {
        System.out.println("Custom Init method of BookCustomBean called !! ");
    }
    public BookCustomBean(String bookName) {
        this.bookName = bookName;
    }
    public String getBookName() {
        return bookName;
    }
    public void setBookName(String bookName) {
        this.bookName = bookName;
    }
    @Override
    public String toString() {
        return "Book{" +
        "bookName='" + bookName + '\'' +
        '}';
    }
}

在前面的代碼中幔翰,customInit和customDestroy是打印輸出消息的常規(guī)方法。
接下來西壮,我們將編寫bean的配置遗增,bean.xml。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- Declare custom init and destroy methods-->
<bean id="customLifeCycleBookBean"
class="guru.springframework.springbeanlifecycle.custominitanddestroy.domain.BookCustomBean"
init-method="customInit"
destroy-method="customDestroy">
<property name="bookName" value="Life and Laughing"></property>
</bean>
</beans>

在前面的代碼中款青,第11行-第12行使用值customInit和customDestroy的init-method和destroy-method屬性做修。

main類的代碼如下:

package guru.springframework.springbeanlifecycle;
import guru.springframework.springbeanlifecycle.custominitanddestroy.domain.BookCustomBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SpringBootApplication
public class SpringBeanLifecycleApplication {
    public static void main(String[] args) {
    SpringApplication.run(SpringBeanLifecycleApplication.class, args);
    // -------custominitanddestroy------
    ApplicationContext context3 =
    new ClassPathXmlApplicationContext("beans.xml");
    BookCustomBean bookCustomBean = (BookCustomBean) context3.getBean("customLifeCycleBookBean");
    ((AbstractApplicationContext) context3).registerShutdownHook();
    }
}

前面的代碼加載XML配置并測(cè)試init-method和destroy-method。
運(yùn)行代碼輸出如下:

  • Constructor of BookCustomBean bean is called !!
  • Custom Init method of BookCustomBean called !!
  • Custom destroy method of BookCustomBean called !!

總結(jié)

所有Spring bean都要經(jīng)歷一個(gè)特定的生命周期抡草,正如我們所看到的饰及,在底層實(shí)際上有很多事情要做。這些大部分都是由框架來處理的康震,作為一個(gè)Spring開發(fā)人員燎含,你很少需要經(jīng)常使用它。然而腿短,當(dāng)你使用Spring框架進(jìn)入越來越復(fù)雜的應(yīng)用程序時(shí)屏箍,有時(shí)您必須了解bean生命周期中發(fā)生的事情。
我個(gè)人不喜歡使用InitializingBean和DisposableBean接口橘忱。主要是因?yàn)樗鼘⒋a與Spring緊密地耦合在一起赴魁。更好的方法是在bean配置文件中指定init-method和destroy-method屬性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钝诚,一起剝皮案震驚了整個(gè)濱河市颖御,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌凝颇,老刑警劉巖潘拱,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異拧略,居然都是意外死亡泽铛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門辑鲤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人杠茬,你說我怎么就攤上這事月褥。” “怎么了瓢喉?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵宁赤,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我栓票,道長(zhǎng)决左,這世上最難降的妖魔是什么愕够? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮佛猛,結(jié)果婚禮上惑芭,老公的妹妹穿的比我還像新娘。我一直安慰自己继找,他們只是感情好遂跟,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著婴渡,像睡著了一般幻锁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上边臼,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天哄尔,我揣著相機(jī)與錄音,去河邊找鬼柠并。 笑死岭接,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的堂鲤。 我是一名探鬼主播亿傅,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼瘟栖!你這毒婦竟也來了葵擎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤半哟,失蹤者是張志新(化名)和其女友劉穎酬滤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寓涨,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盯串,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了戒良。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片体捏。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖糯崎,靈堂內(nèi)的尸體忽然破棺而出几缭,到底是詐尸還是另有隱情,我是刑警寧澤沃呢,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布年栓,位于F島的核電站,受9級(jí)特大地震影響薄霜,放射性物質(zhì)發(fā)生泄漏某抓。R本人自食惡果不足惜纸兔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望否副。 院中可真熱鬧汉矿,春花似錦、人聲如沸副编。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽痹届。三九已至呻待,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間队腐,已是汗流浹背蚕捉。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留柴淘,地道東北人迫淹。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像为严,于是被迫代替她去往敵國(guó)和親敛熬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容