正確理解Java反射的姿勢(shì)

反射簡(jiǎn)介

反射是Java的高級(jí)特性之一,但是在實(shí)際的開(kāi)發(fā)中,使用Java反射的案例卻非常的少酵幕,但是反射確實(shí)在底層框架中被頻繁的使用。

比如:JDBC中的加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序缓苛,Spring框架中加載bean對(duì)象芳撒,動(dòng)以及態(tài)代理,這些都使用到反射未桥,因?yàn)槲覀円肜斫庖恍┛蚣艿牡讓釉肀噬玻瓷涫俏覀儽仨氁莆盏摹?/p>

理解反射我們先從他的概念入手,那么什么是反射呢冬耿?

反射就是在運(yùn)行狀態(tài)能夠動(dòng)態(tài)的獲取該類(lèi)的屬性和方法舌菜,并且能夠任意的使用該類(lèi)的屬性和方法,這種動(dòng)態(tài)獲取類(lèi)信息以及動(dòng)態(tài)的調(diào)用對(duì)象的方法的功能就是反射亦镶。

實(shí)現(xiàn)上面操作的前提是能夠獲取到該類(lèi)的字節(jié)碼對(duì)象日月,也就是.class文件袱瓮,在反射中獲取class文件的方式有三種:

類(lèi)名.class如:Person.class

對(duì)象.getClass如:person.getClass

Class.forName(全類(lèi)名)獲取如:Class.forName("ldc.org.demo.person")

Class對(duì)象

對(duì)于反射的執(zhí)行過(guò)程的原理,我這里畫(huà)了一張圖爱咬,以供大家參考理解尺借。

我們看過(guò)JVM的相關(guān)書(shū)籍都會(huì)詳細(xì)的了解到,Java文件首先要通過(guò)編譯器編譯精拟,編譯成Class文件燎斩,然后通過(guò)類(lèi)加載器(ClassLoader)將class文件加載到JVM中。

在JVM中Class文件都與一個(gè)Class對(duì)象對(duì)應(yīng)蜂绎,在因?yàn)镃lass對(duì)象中包含著該類(lèi)的類(lèi)信息瘫里,只要獲取到Class對(duì)象便可以操作該類(lèi)對(duì)象的屬性與方法。

在這里深入理解反射之前先來(lái)深入的理解Class對(duì)象荡碾,它包含了類(lèi)的相關(guān)信息谨读。

Java中我們?cè)谶\(yùn)行時(shí)識(shí)別對(duì)象和類(lèi)的信息,也叫做RTTI坛吁,方式主要有來(lái)兩種:

傳統(tǒng)的RTTI(Run-Time Type Information)

反射機(jī)制

那么什么是RTTI呢劳殖?RTTI稱(chēng)為運(yùn)行時(shí)類(lèi)型識(shí)別,傳統(tǒng)的RTTI是在編譯時(shí)就已經(jīng)知道所有類(lèi)型拨脉;而反射機(jī)制則是在程序運(yùn)行時(shí)才確定的類(lèi)型信息哆姻。

想要運(yùn)行時(shí)使用類(lèi)型信息,就必須要獲取Class對(duì)象的引用玫膀,獲取Class對(duì)象的方式上面已經(jīng)提及矛缨。

這里有點(diǎn)區(qū)別的就是使用(.class)方式獲取Class對(duì)象,并不會(huì)初始化Class對(duì)象帖旨,而使用(forName("全類(lèi)名"))的方式會(huì)自動(dòng)初始化Class對(duì)象箕昭。

當(dāng)一個(gè).class文件要被加載到JVM中的時(shí)候,會(huì)進(jìn)行如下的準(zhǔn)備工作解阅,首先會(huì)檢查這個(gè)類(lèi)是否被加載落竹,若是沒(méi)有被加載就會(huì)根據(jù)全類(lèi)名找到class文件,接著加載Class文件货抄,并創(chuàng)建類(lèi)的靜態(tài)成員引用述召。

但是在程序中并非是一開(kāi)始就完全加載該類(lèi)的class文件,而是在程序用的地方再加載蟹地,即為懶加載模式积暖。

當(dāng)加載完Class文件后,接著就會(huì)驗(yàn)證Class文件中的字節(jié)碼怪与,并靜態(tài)域分配存儲(chǔ)空間夺刑。這個(gè)過(guò)程也叫做鏈接

最后一步就是進(jìn)行初始化琼梆,即為了使用類(lèi)而提前做的準(zhǔn)備工作如下圖所示:

反射

反射對(duì)應(yīng)到Java中的類(lèi)庫(kù)就是在java.lang.reflect下性誉,? ? 在該包下包含著Field窿吩、Method和Constructor類(lèi)。

Field是表示一個(gè)類(lèi)的屬性信息错览,Method表示類(lèi)的方法信息纫雁,Constructor表示的是類(lèi)的構(gòu)造方法的信息。

在反射中常用的方法倾哺,我這里做了一個(gè)列舉轧邪,當(dāng)然更加詳細(xì)的可以查官方的API文檔進(jìn)行學(xué)習(xí)。

方法名作用

getConstructors()獲取公共構(gòu)造器

getDeclaredConstructors()獲取所有構(gòu)造器

newInstance()獲取該類(lèi)對(duì)象

getName()獲取類(lèi)名包含包路徑

getSimpleName()獲取類(lèi)名不包含包路徑

getFields()獲取類(lèi)公共類(lèi)型的所有屬性

getDeclaredFields()獲取類(lèi)的所有屬性

getField(String name)獲取類(lèi)公共類(lèi)型的指定屬性

getDeclaredField(String name)獲取類(lèi)全部類(lèi)型的指定屬性

getMethods()獲取類(lèi)公共類(lèi)型的方法

getDeclaredMethods()獲取類(lèi)的所有方法

getMethod(String name, Class[] parameterTypes)獲得類(lèi)的特定公共類(lèi)型方法

getDeclaredClasses()獲取內(nèi)部類(lèi)

getDeclaringClass()獲取外部類(lèi)

getPackage()獲取所在包

另外對(duì)于反射的使用這里附上一段小demo羞海,具體的實(shí)際應(yīng)用忌愚,會(huì)在后面繼續(xù)說(shuō)到,并且也會(huì)附上代碼的實(shí)現(xiàn):

publicclassUser{

privateString?name;

privateInteger?age;

publicUser(){

}

publicUser(String?name,?Integer?age){

this.name?=?name;

this.age?=?age;

}

privatevoidprivateMethod(){

System.err.println("privateMethod");

}

publicvoidpublicMethod(String?param){

System.err.println("publicMethod"+param);

}

@Override

publicStringtoString(){

return"User{"+

"name='"+?name?+'\''+

",?age="+?age?+

'}';

}

}

在User的實(shí)體類(lèi)中却邓,有兩個(gè)屬性age和name硕糊,并且除了有兩個(gè)測(cè)試方法privateMethod和publicMethod用于測(cè)試私有方法和公共方法的獲取。接著執(zhí)行如下代碼:

Class?clazz=User.class;

//獲取有參構(gòu)造

Constructor?constructor?=?clazz.getConstructor(String.class,?Integer.class);

//獲取該類(lèi)對(duì)象并設(shè)置屬性的值

Objectobj?=?constructor.newInstance("黎杜",18);

//獲得類(lèi)全類(lèi)名腊徙,既包含包路徑

StringfullClassName?=?clazz.getName();

//獲得類(lèi)名

StringclassName?=?clazz.getSimpleName();

//獲得類(lèi)中公共類(lèi)型(public)屬性

Field[]?fields?=?clazz.getFields();

StringfieldName="";

for(Field?field?:?fields){

//?獲取屬性名

fieldName=field.getName();

System.out.println(fieldName)

}

//獲得類(lèi)中全部類(lèi)型屬性(包括private)

Field[]?fieldsAll?=?clazz.getDeclaredFields();

fieldName="";

for(Field?field?:?fieldsAll){

//?獲取屬性名

fieldName=field.getName();

System.out.println(fieldName)

}

//獲得指定公共屬性值

Field?age?=?clazz.getField("age");

Objectvalue?=?age.get(obj);

System.err.println("公共指定屬性:?"+value);

//獲得指定的私有屬性值

Field?name?=?clazz.getDeclaredField("name");

//設(shè)置為true才能獲取私有屬性

name.setAccessible(true);

Objectvalue2=?name.get(obj);

System.err.println("私有指定屬性值:?"+value2);

//獲取所有公共類(lèi)型方法???這里包括?Object?類(lèi)的一些方法

Method[]?methods?=?clazz.getMethods();

StringmethodsName="";

for(Method?method?:?methods){

methodsName=method.getName();

}

//獲取該類(lèi)中的所有方法(包括private)

Method[]?methodsAll?=?clazz.getDeclaredMethods();

methodsName="";

for(Method?method?:?methodsAll){

methodsName=method.getName();

}

//獲取并使用指定方法

Method?privateMethod=?clazz.getDeclaredMethod("privateMethod");//獲取無(wú)參私有方法

privateMethod.setAccessible(true);

privateMethod.invoke(obj);//調(diào)用方法

Method?publicMethod=?clazz.getMethod("publicMethod",String.class);//獲取有參數(shù)方法

publicMethod.invoke(obj,"黎杜");//調(diào)用有參方法

看完上面的demo以后简十,有些人會(huì)說(shuō),老哥這只是一個(gè)很簡(jiǎn)單的demo撬腾,確實(shí)是螟蝙,這里為了照顧一下一些新手,先熟悉一下反射的一些方法的用法民傻,好戲還在后頭胰默。

反射在jdk 1.5的時(shí)候允許對(duì)Class對(duì)象能夠支持泛型,也稱(chēng)為泛化Class漓踢,具體的使用如下:

Class?user=?User.class;

//泛化class可以直接得到具體的對(duì)象牵署,而不再是Object

Useruser=?user.newInstance();

泛化實(shí)現(xiàn)了在獲取實(shí)例的時(shí)候直接就可以獲取到具體的對(duì)象,因?yàn)樵诰幾g器的時(shí)候就會(huì)做類(lèi)型檢查彭雾。當(dāng)然也可以使用通配符的方式碟刺,例如:Class<?>

反射實(shí)際應(yīng)用

經(jīng)過(guò)上面的反射的原理介紹锁保,下面就要開(kāi)始反射的實(shí)際場(chǎng)景的應(yīng)用薯酝,所有的技術(shù),你知道的該技術(shù)的應(yīng)用場(chǎng)景永遠(yuǎn)是最值錢(qián)爽柒。這個(gè)是越多越好吴菠,知道的場(chǎng)景越多思路就越多。

反射的實(shí)際場(chǎng)景的應(yīng)用浩村,這里主要列舉這幾個(gè)方面:

動(dòng)態(tài)代理

JDBC 的數(shù)據(jù)庫(kù)的連接

Spring 框架的使用

動(dòng)態(tài)代理實(shí)際就是使用反射的技術(shù)來(lái)實(shí)現(xiàn)做葵,在程序運(yùn)行時(shí)創(chuàng)建一個(gè)代理類(lèi),用來(lái)代理給定的接口心墅,實(shí)現(xiàn)動(dòng)態(tài)處理對(duì)其所代理的方法的調(diào)用酿矢。

實(shí)現(xiàn)動(dòng)態(tài)代理主要有以下幾個(gè)步驟:

實(shí)現(xiàn)InvocationHandler接口榨乎,重寫(xiě)invoke方法,實(shí)現(xiàn)被代理對(duì)象的方法調(diào)用的邏輯瘫筐。

Proxy.getProxyClass獲取代理類(lèi)

執(zhí)行方法蜜暑,代理成功

動(dòng)態(tài)代理的實(shí)現(xiàn)代碼如下所示,首先創(chuàng)建自己類(lèi)DynamicProxyHandler實(shí)現(xiàn)InvocationHandler:

publicclassDynamicProxyHandlerimplementsInvocationHandler{

privateObject?targetObj;

publicDynamicProxyHandler(){

super();

}

publicDynamicProxyHandler(Object?targetObj){

super();

this.targetObj=?targetObj;

}

@Override

publicObjectinvoke(Object?proxy,?Method?method,?Object[]?args)throwsThrowable{

System.err.println("開(kāi)始執(zhí)行targetObj的方法");

//執(zhí)行被代理的targetObj的方法

method.invoke(targetObj,?args);

System.out.println("執(zhí)行方法結(jié)束");

returnnull;

}

}

然后執(zhí)行Proxy.newProxyInstance方法創(chuàng)建代理對(duì)象策肝,最后執(zhí)行代理對(duì)象的方法肛捍,代碼實(shí)現(xiàn)如下:

User?user?=newUserImpl();

DynamicProxyHandler?dynamicProxy?=newDynamicProxyHandler(user);

//第一個(gè)參數(shù):類(lèi)加載器;第二個(gè)參數(shù):user.getClass().getInterfaces():被代理對(duì)象的接口之众;第三個(gè)參數(shù):代理對(duì)象

User?userProxy?=?(User?)Proxy.newProxyInstance(user.getClass().getClassLoader(),?user.getClass().getInterfaces(),?dynamicProxy);

userProxy.login();

userProxy.logout();

以上的實(shí)現(xiàn)是jdk的動(dòng)態(tài)代理方式拙毫,還有一種動(dòng)態(tài)代理是Cglib的動(dòng)態(tài)代理方式,Cglib動(dòng)態(tài)代理也是被廣泛的使用棺禾,比如Spring AOP框架中缀蹄,實(shí)現(xiàn)了方法的攔截功能

在ORM框架Hibernate框架也是使用Cglib框架來(lái)代理單端single-ended的關(guān)聯(lián)關(guān)系膘婶。

jdk的動(dòng)態(tài)代理與Cglib的動(dòng)態(tài)代理的區(qū)別在于jdk動(dòng)態(tài)代理必須實(shí)現(xiàn)接口袍患,而Cglib的動(dòng)態(tài)代理是對(duì)那些沒(méi)有實(shí)現(xiàn)接口的類(lèi),實(shí)習(xí)那的原理通通過(guò)繼承稱(chēng)為子類(lèi)竣付,并覆蓋父類(lèi)中的一些方法诡延。

對(duì)于Cglib的動(dòng)態(tài)代理這里由于篇幅的原因不再做詳細(xì)講解,下一篇將會(huì)詳細(xì)的講解jdk的動(dòng)態(tài)代理和Cglib的動(dòng)態(tài)代理的實(shí)現(xiàn)古胆。

下面我們來(lái)看看JDBC中反射的應(yīng)用案例肆良,在JDBC中使用Class.forName()方法來(lái)加載數(shù)據(jù)庫(kù)驅(qū)動(dòng),就是使用反射的案例逸绎。

讓我們來(lái)一波入門(mén)的時(shí)候?qū)懙拇a惹恃,一波回憶殺歷歷在目,具體的實(shí)現(xiàn)代碼我相信也是很多人在初學(xué)者的時(shí)候也寫(xiě)過(guò)棺牧,如下所示:

Class.forName("com.mysql.jdbc.Driver");//1巫糙、使用CLASS?類(lèi)加載驅(qū)動(dòng)程序?,反射機(jī)制的體現(xiàn)?

con?=?DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test","root","root");//2、連接數(shù)據(jù)庫(kù)??

最后一個(gè)案例實(shí)現(xiàn)是使用反射模擬Spring通過(guò)xml文件初始化Bean的過(guò)程颊乘,學(xué)過(guò)ssm的項(xiàng)目都會(huì)依稀的記得Spring的配置文件参淹,比如:

上面的配置文件非常的熟悉,在標(biāo)簽里面有屬性乏悄,屬性有屬性值浙值,以及標(biāo)簽還有子標(biāo)簽,子標(biāo)簽也有屬性和屬性值檩小,那么怎么用他們初始化成Bean呢开呐?

思路可以是這樣的,首先得得到配置文件的位置,然后加載配置文件筐付,加載配置文件后就可以解析具體的標(biāo)簽卵惦,獲取到屬性和屬性值,通過(guò)屬性值初始化Bean瓦戚。

實(shí)現(xiàn)的代碼如下鸵荠,首先加載配置文件的內(nèi)容,并獲取到配置文件的根節(jié)點(diǎn):

SAXReaderreader?=?new?SAXReader();

ClassLoaderclassLoader?=?Thread.currentThread().getContextClassLoader();

InputStreamis=?classLoader.getResourceAsStream(beanXml);

Documentdoc?=?reader.read(is);

Elementroot?=?doc.getRootElement();

拿到根節(jié)點(diǎn)后伤极,然后可以獲取bean標(biāo)簽中的屬性和屬性值蛹找,當(dāng)拿到屬性class屬性值后就可以通過(guò)反射初始化Bean對(duì)象。

for(Iteratori?=?root.elementIterator("bean");?i.hasNext();)?{

Elementfoo?=?(Element)?i.next();

//獲取Bean中的屬性值

Attribute?idValue?=?foo.attribute("id");

Attribute?clazzValue?=?foo.attribute("class");

//通過(guò)反射獲取Class對(duì)象

Class?bean?=?Class.forName(clazzValue.getText());

//并實(shí)例化Bean對(duì)象

Objectobj?=?bean.newInstance();

}

除了初始化對(duì)象你還可以為Bean對(duì)象賦予初始值哨坪,例如上面的bean標(biāo)簽下還有property標(biāo)簽庸疾,以及它的屬性值value:

我們就可以通過(guò)一下代碼來(lái)初始化這些值:

BeanInfo?beanInfo?=?Introspector.getBeanInfo(bean);

//?bean對(duì)象的屬性信息

PropertyDescriptor?propertyDescriptor[]?=?beanInfo?.getPropertyDescriptors();

for(Iteratorite?=?foo.elementIterator("property");?ite.hasNext();)?{

Elementproperty=?(Element)?ite.next();

Attribute?name?=?property.attribute("name");

Attribute?value?=?property.attribute("value");

for(inti=0;?k?<?propertyDescriptor.length;?i++)?{

if(propertyDescriptor[i].getName().equalsIgnoreCase(name.getText()))?{

Method?method=?propertyDescriptor[i].getWriteMethod();

//使用反射將值設(shè)置進(jìn)去

method.invoke(obj,?value.getText());

}

}

以上就是簡(jiǎn)單的三個(gè)反射的應(yīng)用案例,也是比較簡(jiǎn)單当编,大佬不喜勿噴哈届慈,初學(xué)者就當(dāng)是自己學(xué)多一點(diǎn)知識(shí),總之一點(diǎn)一點(diǎn)進(jìn)步忿偷。

反射優(yōu)點(diǎn)和缺點(diǎn)

優(yōu)點(diǎn):反射可以動(dòng)態(tài)的獲取對(duì)象金顿,調(diào)用對(duì)象的方法和屬性,并不是寫(xiě)死的鲤桥,比較靈活揍拆,比如你要實(shí)例化一個(gè)bean對(duì)象,你可能會(huì)使用new User()寫(xiě)死在代碼中茶凳。

但是使用反射就可以使用class.forName(user).newInstance()嫂拴,而變量名user可以寫(xiě)在xml配置文件中,這樣就不用修改源代碼贮喧,靈活筒狠、可配置

缺點(diǎn):反射的性能問(wèn)題一直是被吐槽的地方箱沦,反射是一種解釋操作辩恼,用于屬性字段和方法的接入時(shí)要遠(yuǎn)遠(yuǎn)慢于直接使用代碼,因此普通程序也很少使用反射谓形。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末灶伊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子套耕,更是在濱河造成了極大的恐慌谁帕,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冯袍,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)康愤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)儡循,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人征冷,你說(shuō)我怎么就攤上這事择膝。” “怎么了检激?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵肴捉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我叔收,道長(zhǎng)齿穗,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任饺律,我火速辦了婚禮窃页,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘复濒。我一直安慰自己脖卖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布巧颈。 她就那樣靜靜地躺著畦木,像睡著了一般。 火紅的嫁衣襯著肌膚如雪砸泛。 梳的紋絲不亂的頭發(fā)上馋劈,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音晾嘶,去河邊找鬼妓雾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛垒迂,可吹牛的內(nèi)容都是我干的械姻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼机断,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼楷拳!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起吏奸,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤欢揖,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后奋蔚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體她混,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡烈钞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坤按。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片毯欣。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖臭脓,靈堂內(nèi)的尸體忽然破棺而出酗钞,到底是詐尸還是另有隱情直焙,我是刑警寧澤川背,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站禁荸,受9級(jí)特大地震影響嘹锁,放射性物質(zhì)發(fā)生泄漏葫录。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一兼耀、第九天 我趴在偏房一處隱蔽的房頂上張望压昼。 院中可真熱鬧,春花似錦瘤运、人聲如沸窍霞。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)但金。三九已至,卻和暖如春郁季,著一層夾襖步出監(jiān)牢的瞬間冷溃,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工梦裂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留似枕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓年柠,卻偏偏與公主長(zhǎng)得像凿歼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子冗恨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354