Java反射及代理模式

反射

反射是Java語言的重要特性烤惊,它允許程序運(yùn)行時(shí)進(jìn)行自我檢查魏颓,也允許對內(nèi)部的成員
進(jìn)行操作忘瓦,能夠?qū)崿F(xiàn)在運(yùn)行時(shí)對類進(jìn)行裝載格仲,使程序運(yùn)行時(shí)更加靈活辜荠,但是也有注意正確使用
否則會對性能有影響。

案例1 基本的反射

父類

public class Parents {
    public void function(){
        System.out.println("I'm parents!");
    }
}

子類

public class Children extends Parents{
    @Override
    public void function() {
        System.out.println("I'm children!");;
    }
}

測試類

import one.Parents;public class TestReflection {
    @org.junit.Test
    public void testFunction() throws Exception{
        Class cls = Class.forName("one.Children");// 包名.類名
        Parents parents = (Parents)cls.newInstance();
        parents.function();
    }
}

這時(shí)候會調(diào)用子類的function方法輸出

I'm children!

我們可以寫一個(gè)父類的工廠方法抓狭,傳入子類的名稱,自動調(diào)用子類的方法造烁,一些RPC框架就是基于這種動態(tài)代理的思想

這里我們增加一個(gè)實(shí)現(xiàn)類Student

public class Student extends Parents{
    @Override
    public void function() {
        System.out.println("I'm children!");;
    }
}

增加一個(gè)工廠否过,模擬注冊中心,調(diào)用子類的方法

public class Factory {
    private static Map<String, String> map = new HashMap<String, String>();

    static{
        map.clear();
        // 模擬注冊中心惭蟋,存放實(shí)現(xiàn)類
        map.put("one.Children", "one.Children");
        map.put("one.Student", "one.Student");
    }

    public static void main(String[] args) {
        run("one.Student");
    }

    private static
    void run(String clzName){
        if(null==clzName || clzName.length()==0 || "".equals(clzName)){
            System.out.println("參數(shù)不能為空");
            return;
        }
        if(!map.containsKey(clzName)){
            System.out.println("參數(shù)不合法");
            return;
        }
        try{
            Class cls = Class.forName(map.get(clzName));
            Parents parents = (Parents)cls.newInstance();
            System.out.println("執(zhí)行方法"+clzName);
            parents.function();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

運(yùn)行Factory會出現(xiàn)

執(zhí)行方法Student
I'm children!

案例2 動態(tài)代理實(shí)現(xiàn)反射

父類接口

public interface Parents {
    public void function();
}

子類實(shí)現(xiàn)

public class Children implements Parents{
    @Override
    public void function() {
        System.out.println("I'm children!");
    }
}

基本的代理類苗桂,繼承InvocationHandler接口,實(shí)現(xiàn)invoke方法告组。

public class Intermediary implements InvocationHandler {
    private Object post;
    public Intermediary(Object post) {
        this.post = post;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = method.invoke(post, args);
        System.out.println("代理類:打印日志");
        return invoke;
    }
}

優(yōu)雅的代理類煤伟,直接返回對象。采用內(nèi)部類的方式木缝,將反射的方法當(dāng)作參數(shù)傳遞給InvocationHandler

public class GetObject {
    /**
     * 反射+動態(tài)代理的方式調(diào)用Parents里面的方法
     * @return
     */
    public static void runObject(final Parents post){
        // 調(diào)用方法時(shí)的處理器便锨,本質(zhì)都是在調(diào)用invoke()方法
        InvocationHandler h = new InvocationHandler() {
            /**
             * 調(diào)用方法的處理內(nèi)容放在invoke里面
             * @param proxy 代理對象
             * @param method 調(diào)用的方法
             * @param args 傳遞的參數(shù)
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object invoke = method.invoke(post, args);
                System.out.println("代理類:打印日志");
                return invoke;
            }
        };
        // 參數(shù):產(chǎn)生這個(gè)代理類的classLoader, 實(shí)現(xiàn)了這個(gè)代理類的接口,h
        Object o = Proxy.newProxyInstance(Parents.class.getClassLoader(), new Class[]{Parents.class}, h);
        System.out.println(o.getClass().getName());
        System.out.println(o.getClass().getInterfaces()[0]);
        Parents parents = (Parents) o;
        parents.function();
        return;
    }
}

測試下上述兩個(gè)類的效果

public class TestIntermediary {
    public static void main(String[] args) {

        // 基本的代理類
        Parents child = new Children();
        Intermediary intermediary = new Intermediary(child);
        Parents proxy = (Parents) Proxy.newProxyInstance(child.getClass().getClassLoader(), child.getClass().getInterfaces(), intermediary);
        proxy.function();

        // 優(yōu)雅的代理工具類
        GetObject.runObject(new Children());
    }
}

通過代理類可以在代理類中包裝一些方法進(jìn)去我碟,以下是運(yùn)行結(jié)果

I'm children!
代理類:打印日志
com.sun.proxy.$Proxy0
interface two.Parents
I'm children!
代理類:打印日志

案例3 通過代理類設(shè)置方法攔截器

自定義一個(gè)服務(wù)

public class MyService {
    public String run(String something){
        // int i = 1/0;
        return "服務(wù)正在運(yùn)行...." + something;
    }
}

定義一個(gè)其他服務(wù)

public class OtherService {
    public String runOther(String something){
        // int i = 1/0;
        return "服務(wù)正在運(yùn)行...." + something;
    }
}

寫一個(gè)服務(wù)攔截器放案,對不是“run”的方法進(jìn)行攔截

public class ServiceInterceptor implements MethodInterceptor {

    private static final String INTERCEPTOR = "run";

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        Object result = null;
        if(!INTERCEPTOR.equals(methodInvocation.getMethod().getName())){
            System.out.println("不能執(zhí)行該方法" + methodInvocation.getMethod().getName());
            return null;
        }
        try {
            System.out.println("方法執(zhí)行之前:" + methodInvocation.getMethod().toString());

            result = methodInvocation.proceed();
            System.out.println("方法正常運(yùn)行結(jié)果:" + result);

            System.out.println("方法執(zhí)行之后:" + methodInvocation.getMethod().toString());
            return result;

        } catch (Exception e) {
            System.out.println("方法出現(xiàn)異常:" + e.toString());
            System.out.println("方法運(yùn)行Exception結(jié)果:" + result);
            return result;
        }
    }
}
public class TestMethodInterceptor {
    public static void main(String[] args) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(new MyService());
        proxyFactory.addAdvice(new ServiceInterceptor());

        Object proxy = proxyFactory.getProxy();
        MyService myService = (MyService) proxy;
        myService.run("通過代理工廠設(shè)置代理對象,攔截代理方法");

        proxyFactory.setTarget(new OtherService());
        proxy = proxyFactory.getProxy();
        OtherService otherService = (OtherService) proxy;
        otherService.runOther("通過代理工廠設(shè)置代理對象矫俺,攔截代理方法");
    }
}

可以看到otherService.runOther方法被攔截了

方法執(zhí)行之前:public java.lang.String three.MyService.run(java.lang.String)
方法正常運(yùn)行結(jié)果:服務(wù)正在運(yùn)行....通過代理工廠設(shè)置代理對象,攔截代理方法
方法執(zhí)行之后:public java.lang.String three.MyService.run(java.lang.String)
不能執(zhí)行該方法runOther

案例4 抽象服務(wù)的接口

定義一個(gè)父類實(shí)現(xiàn)接口打印

public class BaseService {
    /**
     *
     * @param something
     * @return
     */
    String run(String something){
        return null;
    }
    void printLog(){
        System.out.println("統(tǒng)一打印日志");
    }
}

ServiceOne實(shí)現(xiàn)上述接口

public class ServiceOne extends BaseService {
    @Override
    String run(String something) {
        return "ServiceOne "+ something;
    }
}

ServiceTwo實(shí)現(xiàn)上述接口

public class ServiceTwo extends BaseService{

    @Override
    String run(String something) {
        return "ServiceTwo "+ something;
    }
}

借助Spring攔截器据忘,實(shí)現(xiàn)反射

public class ServiceInterceptor implements MethodInterceptor {

    private static final String INTERCEPTOR = "printLog";

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        Object result = null;
        try {
            if(INTERCEPTOR.equals(methodInvocation.getMethod().getName())){
                //日志直接打印
                result = methodInvocation.proceed();
                return result;
            }

            System.out.println("方法執(zhí)行之前:" + methodInvocation.getMethod().toString());

            result = methodInvocation.proceed();
            System.out.println("方法正常運(yùn)行結(jié)果:" + result);

            System.out.println("方法執(zhí)行之后:" + methodInvocation.getMethod().toString());
            return result;

        } catch (Exception e) {
            System.out.println("方法出現(xiàn)異常:" + e.toString());
            System.out.println("方法運(yùn)行Exception結(jié)果:" + result);
            return result;
        }
    }
}

定義測試類扣泊,調(diào)用ServiceOne和ServiceTwo, 把二者的公共方法printLog直接放在test里面調(diào)用,反射不會對其執(zhí)行環(huán)繞方法稿湿。

public class TestMethodInterceptor {
    public static void main(String[] args) {
        ServiceOne serviceOne = (ServiceOne) test(new ServiceOne());
        serviceOne.run("通過代理工廠設(shè)置代理對象,攔截代理方法");

        ServiceTwo serviceTwo = (ServiceTwo) test(new ServiceTwo());
        serviceTwo.run("通過代理工廠設(shè)置代理對象押赊,攔截代理方法");
    }

    public static BaseService test(BaseService baseService){
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(baseService);
        proxyFactory.addAdvice(new ServiceInterceptor());

        Object proxy = proxyFactory.getProxy();
        BaseService baseProxy = (BaseService) proxy;
        baseProxy.printLog();
        return baseProxy;
    }
}

查看執(zhí)行結(jié)果

統(tǒng)一打印日志
方法執(zhí)行之前:java.lang.String four.ServiceOne.run(java.lang.String)
方法正常運(yùn)行結(jié)果:ServiceOne 通過代理工廠設(shè)置代理對象饺藤,攔截代理方法
方法執(zhí)行之后:java.lang.String four.ServiceOne.run(java.lang.String)
統(tǒng)一打印日志
方法執(zhí)行之前:java.lang.String four.ServiceTwo.run(java.lang.String)
方法正常運(yùn)行結(jié)果:ServiceTwo 通過代理工廠設(shè)置代理對象,攔截代理方法
方法執(zhí)行之后:java.lang.String four.ServiceTwo.run(java.lang.String)

案例5 使用FastClass完成反射考杉,執(zhí)行類中的所有方法

父接口與原來保持一致

public interface Parents {
    String hello(String name);
}

子類實(shí)現(xiàn)接口

public class Children implements Parents {

    @Override
    public String hello(String name) {
        return "I'm " + name;
    }
}

實(shí)現(xiàn)基本的代理類

public class Intermediary implements InvocationHandler {

    private Object post;

    public Intermediary(Object post) {
        this.post = post;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = method.invoke(post, args);
        System.out.println("代理類:打印日志");
        return invoke;
    }
}

調(diào)用FastClass實(shí)現(xiàn)反射策精,循環(huán)遍歷類中的方法執(zhí)行

public class GetObject {

    /**
     * 反射+動態(tài)代理的方式調(diào)用Parents里面的方法
     * @return
     */
    public static Object runObject(final Parents post, String methodName, Object[] parameters){
        try {
            Class<?> clazz = Class.forName(post.getClass().getName());
            //獲取本類的所有方法,存放入數(shù)組
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                if(methodName.equals(method.getName())){
                    System.out.println("方法名:"+method.getName());
                    //獲取本方法所有參數(shù)類型崇棠,存入數(shù)組
                    Class<?>[] getTypeParameters = method.getParameterTypes();
                    if(getTypeParameters.length==0){
                        System.out.println("此方法無參數(shù)");
                    }
                    for (Class<?> class1 : getTypeParameters) {
                        String parameterName = class1.getName();
                        System.out.println("參數(shù)類型:"+parameterName);
                    }
                    FastClass fastClazz = FastClass.create(Parents.class);
                    int methodIndex = fastClazz.getIndex(method.getName(), getTypeParameters);
                    return fastClazz.invoke(methodIndex, post, parameters);
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

執(zhí)行代理類

public class TestIntermediary {
    public static void main(String[] args) {

        // 基本的代理類
        Parents child = new Children();
        Intermediary intermediary = new Intermediary(child);
        Parents proxy = (Parents) Proxy.newProxyInstance(child.getClass().getClassLoader(), child.getClass().getInterfaces(), intermediary);
        System.out.println(proxy.hello("shgx"));

        // 優(yōu)雅的代理工具類
        System.out.println(GetObject.runObject(new Children(), "hello", new String[]{"shgx"}));
    }
}

執(zhí)行結(jié)果如下:

代理類:打印日志
I'm shgx
方法名:hello
參數(shù)類型:java.lang.String
I'm shgx

案例6 使用PropertyUtil和BeanUtil拷貝參數(shù)

父類接口

public interface Parents {
    void function();
}

子類-外部類

public class OuterClass implements Parents{
    private Long id;
    private String name;
    private Integer sex;
    private Double age;
    private Date birthDay;
    // 省略get set toString方法

    @Override
    public void function() {
        System.out.println("OuterClass{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex=" + sex +
                ", age=" + age +
                ", birthDay=" + birthDay +
                '}');
    }
}

子類-內(nèi)部類

public class InnerClass implements Parents {
    private Long id;
    private String name;
    private Integer sex;
    private Double age;
    private Date birthDay;
    // 新增, 與上面的類形成對比
    private String address;
    // 省略get set toString方法

    @Override
    public void function() {
        System.out.println("反射實(shí)現(xiàn) InnerClass{" +
                "id=" + getId() +
                ", name='" + getName() + '\'' +
                ", sex=" + getSex() +
                ", age=" + getAge() +
                ", birthDay=" + getBirthDay() +
                ", address='" + address + '\'' +
                '}');
    }
}

優(yōu)雅的代理類實(shí)現(xiàn)反射

public class GetObject {

    /**
     * 反射+動態(tài)代理的方式調(diào)用Parents里面的方法
     * @return
     */
    public static void runObject(final Parents post){
        // 調(diào)用方法時(shí)的處理器咽袜,本質(zhì)都是在調(diào)用invoke()方法
        InvocationHandler h = new InvocationHandler() {
            /**
             * 調(diào)用方法的處理內(nèi)容放在invoke里面
             * @param proxy 代理對象
             * @param method 調(diào)用的方法
             * @param args 傳遞的參數(shù)
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object invoke = method.invoke(post, args);
                return invoke;
            }
        };
        // 參數(shù):產(chǎn)生這個(gè)代理類的classLoader, 實(shí)現(xiàn)了這個(gè)代理類的接口,h
        Object o = Proxy.newProxyInstance(Parents.class.getClassLoader(), new Class[]{Parents.class}, h);
        Parents parents = (Parents) o;
        parents.function();
    }
}

實(shí)現(xiàn)對象之間的參數(shù)拷貝

public class CopyUtil {

    @SuppressWarnings("unchecked")
    public static void getClassByBeanUtil(InnerClass dest, OuterClass source) {
        try {
            BeanUtils.copyProperties(dest, source);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        System.out.println("BeanUtils result:" + dest);
    }

    public static void getClassByPropertyUtil(InnerClass dest, OuterClass source) {
        try {
            PropertyUtils.copyProperties(dest, source);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        System.out.println("PropertyUtils result:" + dest);
    }
}
public class TestIntermediary {
    public static void main(String[] args) {

        // 基本的代理類
        OuterClass outerClass = new OuterClass(1L, "shgx", 1, 23.5, new Date());
        // BeanUtil 完成對象拷貝
        InnerClass innerClassOne = new InnerClass();
        CopyUtil.getClassByBeanUtil(innerClassOne, outerClass);
        GetObject.runObject(innerClassOne);

        // PropertyUtil 完成對象拷貝
        InnerClass innerClassTwo = new InnerClass();
        CopyUtil.getClassByPropertyUtil(innerClassTwo, outerClass);
        GetObject.runObject(innerClassTwo);

        // 置空
        outerClass.setId(null);
        outerClass.setName(null);
        outerClass.setAge(null);
        outerClass.setSex(null);
        outerClass.setBirthDay(null);
        // BeanUtil
        CopyUtil.getClassByBeanUtil(innerClassOne, outerClass);
        GetObject.runObject(innerClassOne);

        // PropertyUtil
        CopyUtil.getClassByPropertyUtil(innerClassTwo, outerClass);
        GetObject.runObject(innerClassTwo);

    }
}

執(zhí)行結(jié)果

BeanUtils result:InnerClass{id=1, name='shgx', sex=1, age=23.5, birthDay=Sat Jul 04 18:23:51 CST 2020, address='null'}
反射實(shí)現(xiàn) InnerClass{id=1, name='shgx', sex=1, age=23.5, birthDay=Sat Jul 04 18:23:51 CST 2020, address='null'}
PropertyUtils result:InnerClass{id=1, name='shgx', sex=1, age=23.5, birthDay=Sat Jul 04 18:23:51 CST 2020, address='null'}
反射實(shí)現(xiàn) InnerClass{id=1, name='shgx', sex=1, age=23.5, birthDay=Sat Jul 04 18:23:51 CST 2020, address='null'}
BeanUtils result:InnerClass{id=null, name='null', sex=null, age=null, birthDay=null, address='null'}
反射實(shí)現(xiàn) InnerClass{id=null, name='null', sex=null, age=null, birthDay=null, address='null'}
PropertyUtils result:InnerClass{id=null, name='null', sex=null, age=null, birthDay=null, address='null'}
反射實(shí)現(xiàn) InnerClass{id=null, name='null', sex=null, age=null, birthDay=null, address='null'}

上述方法可以在不同系統(tǒng)之間完成參數(shù)傳輸枕稀,實(shí)現(xiàn)RPC調(diào)用

置空之后在執(zhí)行發(fā)現(xiàn)對象拷貝完成也是null询刹,不會賦予默認(rèn)值

多余的參數(shù)會賦予null值,不會拋出異常

案例7 使用注解和反射實(shí)現(xiàn)日志打印

使用注解的方式配置日志

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LogAnnotation {
    /** 日志類型 */
    LogTypeEnum logType() default LogTypeEnum.SERVICE_LOG;

    /** 業(yè)務(wù)名 */
    String bizName() default "";

    /** 自定義日志打印*/
    Class<? extends LogInfo> customerLogType() default LogInfo.class;

    /** 是否打印日志*/
    boolean recordMonitorData() default false;
}

不在展開描述萎坷,見項(xiàng)目空間

源碼參考

項(xiàng)目代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凹联,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子哆档,更是在濱河造成了極大的恐慌蔽挠,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓜浸,死亡現(xiàn)場離奇詭異澳淑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)插佛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門杠巡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人雇寇,你說我怎么就攤上這事氢拥。” “怎么了锨侯?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵嫩海,是天一觀的道長。 經(jīng)常有香客問我识腿,道長出革,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任渡讼,我火速辦了婚禮骂束,結(jié)果婚禮上耳璧,老公的妹妹穿的比我還像新娘。我一直安慰自己展箱,他們只是感情好旨枯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著混驰,像睡著了一般攀隔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上栖榨,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天昆汹,我揣著相機(jī)與錄音,去河邊找鬼婴栽。 笑死满粗,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的愚争。 我是一名探鬼主播映皆,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼轰枝!你這毒婦竟也來了捅彻?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤鞍陨,失蹤者是張志新(化名)和其女友劉穎步淹,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诚撵,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贤旷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了砾脑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡艾杏,死狀恐怖韧衣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情购桑,我是刑警寧澤畅铭,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站勃蜘,受9級特大地震影響硕噩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缭贡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一炉擅、第九天 我趴在偏房一處隱蔽的房頂上張望辉懒。 院中可真熱鬧,春花似錦谍失、人聲如沸眶俩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽颠印。三九已至,卻和暖如春抹竹,著一層夾襖步出監(jiān)牢的瞬間线罕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工窃判, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钞楼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓兢孝,卻偏偏與公主長得像窿凤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子跨蟹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354