代理模式

Start

前言

轉(zhuǎn)載

為其他對(duì)象提供一個(gè)代理以控制對(duì)某個(gè)對(duì)象的訪問(wèn)易猫。代理類(lèi)主要負(fù)責(zé)為委托了(真實(shí)對(duì)象)預(yù)處理消息鼓寺、過(guò)濾消息皇帮、傳遞消息給委托類(lèi)蹬碧,代理類(lèi)不現(xiàn)實(shí)具體服務(wù)皇筛,而是利用委托類(lèi)來(lái)完成服務(wù),并將執(zhí)行結(jié)果封裝處理社痛。

其實(shí)就是代理類(lèi)為被代理類(lèi)預(yù)處理消息见转、過(guò)濾消息并在此之后將消息轉(zhuǎn)發(fā)給被代理類(lèi),之后還能進(jìn)行消息的后置處理蒜哀。代理類(lèi)和被代理類(lèi)通常會(huì)存在關(guān)聯(lián)關(guān)系(即上面提到的持有的被帶離對(duì)象的引用)斩箫,代理類(lèi)本身不實(shí)現(xiàn)服務(wù),而是通過(guò)調(diào)用被代理類(lèi)中的方法來(lái)提供服務(wù)。

1. 靜態(tài)代理

創(chuàng)建一個(gè)接口乘客,然后創(chuàng)建被代理的類(lèi)實(shí)現(xiàn)該接口并且實(shí)現(xiàn)該接口中的抽象方法狐血。之后再創(chuàng)建一個(gè)代理類(lèi),同時(shí)使其也實(shí)現(xiàn)這個(gè)接口易核。在代理類(lèi)中持有一個(gè)被代理對(duì)象的引用匈织,而后在代理類(lèi)方法中調(diào)用該對(duì)象的方法。

接口:

public interface HelloInterface {
    void sayHello();
}

被代理類(lèi):

public class Hello implements HelloInterface{
    @Override
    public void sayHello() {
        System.out.println("Hello zhanghao!");
    }
}

代理類(lèi)

public class HelloProxy implements HelloInterface{
    private HelloInterface helloInterface = new Hello();
    @Override
    public void sayHello() {
        System.out.println("Before invoke sayHello" );
        helloInterface.sayHello();
        System.out.println("After invoke sayHello");
    }
}

代理類(lèi)調(diào)用:
被代理類(lèi)被傳遞給了代理類(lèi) HelloProxy耸成,代理類(lèi)在執(zhí)行具體方法時(shí)通過(guò)所持用的被代理類(lèi)完成調(diào)用。

    public static void main(String[] args) {
        HelloProxy helloProxy = new HelloProxy();
        helloProxy.sayHello();
    }
    
輸出:
Before invoke sayHello
Hello zhanghao!
After invoke sayHello

使用靜態(tài)代理很容易就完成了對(duì)一個(gè)類(lèi)的代理操作浴鸿。但是靜態(tài)代理的缺點(diǎn)也暴露了出來(lái):由于代理只能為一個(gè)類(lèi)服務(wù)井氢,如果需要代理的類(lèi)很多,那么就需要編寫(xiě)大量的代理類(lèi)岳链,比較繁瑣花竞。

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

利用反射機(jī)制在運(yùn)行時(shí)創(chuàng)建代理類(lèi)。
接口掸哑、被代理類(lèi)不變约急,我們構(gòu)建一個(gè) handler 類(lèi)來(lái)實(shí)現(xiàn) InvocationHandler 接口。

public class ProxyHandler implements InvocationHandler{
    private Object object;
    public ProxyHandler(Object object){
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before invoke "  + method.getName());
        method.invoke(object, args);
        System.out.println("After invoke " + method.getName());
        return null;
    }
}

執(zhí)行動(dòng)態(tài)代理:

    public static void main(String[] args) {
        System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        HelloInterface hello = new Hello();
        
        InvocationHandler handler = new ProxyHandler(hello);

        HelloInterface proxyHello = (HelloInterface) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), handler);

        proxyHello.sayHello();
    }
    輸出:
    Before invoke sayHello
    Hello zhanghao!
    After invoke sayHello

通過(guò) Proxy 類(lèi)的靜態(tài)方法 newProxyInstance 返回一個(gè)接口的代理實(shí)例苗分。針對(duì)不同的代理類(lèi)厌蔽,傳入相應(yīng)的代理程序控制器 InvocationHandler。
如果新來(lái)一個(gè)被代理類(lèi) Bye摔癣,如:

public interface ByeInterface {
    void sayBye();
}
public class Bye implements ByeInterface {
    @Override
    public void sayBye() {
        System.out.println("Bye zhanghao!");
    }
}

那么執(zhí)行過(guò)程:

    public static void main(String[] args) {
        System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        HelloInterface hello = new Hello();
        ByeInterface bye = new Bye();

        InvocationHandler handler = new ProxyHandler(hello);
        InvocationHandler handler1 = new ProxyHandler(bye);

        HelloInterface proxyHello = (HelloInterface) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), handler);

        ByeInterface proxyBye = (ByeInterface) Proxy.newProxyInstance(bye.getClass().getClassLoader(), bye.getClass().getInterfaces(), handler1);
        proxyHello.sayHello();
        proxyBye.sayBye();
    }
    輸出:
    Before invoke sayHello
    Hello zhanghao!
    After invoke sayHello
    Before invoke sayBye
    Bye zhanghao!
    After invoke sayBye

3. 動(dòng)態(tài)代理底層實(shí)現(xiàn)

動(dòng)態(tài)代理具體步驟:

  1. 通過(guò)實(shí)現(xiàn) InvocationHandler 接口創(chuàng)建自己的調(diào)用處理器奴饮;
  2. 通過(guò)為 Proxy 類(lèi)指定 ClassLoader 對(duì)象和一組 interface 來(lái)創(chuàng)建動(dòng)態(tài)代理類(lèi);
  3. 通過(guò)反射機(jī)制獲得動(dòng)態(tài)代理類(lèi)的構(gòu)造函數(shù)择浊,其唯一參數(shù)類(lèi)型是調(diào)用處理器接口類(lèi)型戴卜;
  4. 通過(guò)構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理類(lèi)實(shí)例,構(gòu)造時(shí)調(diào)用處理器對(duì)象作為參數(shù)被傳入琢岩。

既然生成代理對(duì)象是用的 Proxy 類(lèi)的靜態(tài)方 newProxyInstance投剥,那么我們就去它的源碼里看一下它到底都做了些什么?

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
         //生成代理類(lèi)對(duì)象
        Class<?> cl = getProxyClass0(loader, intfs);

        //使用指定的調(diào)用處理程序獲取代理類(lèi)的構(gòu)造函數(shù)對(duì)象
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            //如果Class作用域?yàn)樗接械?祝ㄟ^(guò) setAccessible 支持訪問(wèn)
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //獲取Proxy Class構(gòu)造函數(shù)江锨,創(chuàng)建Proxy代理實(shí)例。
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

利用 getProxyClass0(loader, intfs) 生成代理類(lèi) Proxy 的 Class 對(duì)象糕篇。

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        //如果接口數(shù)量大于65535泳桦,拋出非法參數(shù)錯(cuò)誤
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

       
        //如果指定接口的代理類(lèi)已經(jīng)存在與緩存中,則不用新創(chuàng)建娩缰,直接從緩存中取即可灸撰;
        //如果緩存中沒(méi)有指定代理對(duì)象,則通過(guò)ProxyClassFactory來(lái)創(chuàng)建一個(gè)代理對(duì)象。
        return proxyClassCache.get(loader, interfaces);
    }

ProxyClassFactory 內(nèi)部類(lèi)創(chuàng)建浮毯、定義代理類(lèi)完疫,返回給定 ClassLoader 和 interfaces 的代理類(lèi)。

        private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
        // 代理類(lèi)的名字的前綴統(tǒng)一為“$Proxy”
        private static final String proxyClassNamePrefix = "$Proxy";

        // 每個(gè)代理類(lèi)前綴后面都會(huì)跟著一個(gè)唯一的編號(hào)债蓝,如$Proxy0壳鹤、$Proxy1、$Proxy2
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                //驗(yàn)證類(lèi)加載器加載接口得到對(duì)象是否與由apply函數(shù)參數(shù)傳入的對(duì)象相同
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                //驗(yàn)證這個(gè)Class對(duì)象是不是接口
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * 
             * 生成指定代理類(lèi)的字節(jié)碼文件
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

一系列檢查后饰迹,調(diào)用 ProxyGenerator.generateProxyClass 來(lái)生成字節(jié)碼文件芳誓。

  public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        // 真正用來(lái)生成代理類(lèi)字節(jié)碼文件的方法在這里
        final byte[] var4 = var3.generateClassFile();
        // 保存代理類(lèi)的字節(jié)碼文件
        if(saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if(var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar), new String[0]);
                            Files.createDirectories(var3, new FileAttribute[0]);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class", new String[0]);
                        }

                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }

        return var4;
    }

生成代理類(lèi)字節(jié)碼文件的generateClassFile方法:

    private byte[] generateClassFile() {
        //下面一系列的addProxyMethod方法是將接口中的方法和Object中的方法添加到代理方法中(proxyMethod)
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        Class[] var1 = this.interfaces;
        int var2 = var1.length;

        int var3;
        Class var4;
        //獲得接口中所有方法并添加到代理方法中
        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                this.addProxyMethod(var8, var4);
            }
        }

        Iterator var11 = this.proxyMethods.values().iterator();

        List var12;
        while(var11.hasNext()) {
            var12 = (List)var11.next();
            checkReturnTypes(var12);
        }

        Iterator var15;
        try {
            //生成代理類(lèi)的構(gòu)造函數(shù)
            this.methods.add(this.generateConstructor());
            var11 = this.proxyMethods.values().iterator();

            while(var11.hasNext()) {
                var12 = (List)var11.next();
                var15 = var12.iterator();
                    
                while(var15.hasNext()) {
                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                    this.methods.add(var16.generateMethod());
                }
            }

            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }

        if(this.methods.size() > '\uffff') {
            throw new IllegalArgumentException("method limit exceeded");
        } else if(this.fields.size() > '\uffff') {
            throw new IllegalArgumentException("field limit exceeded");
        } else {
            this.cp.getClass(dotToSlash(this.className));
            this.cp.getClass("java/lang/reflect/Proxy");
            var1 = this.interfaces;
            var2 = var1.length;

            for(var3 = 0; var3 < var2; ++var3) {
                var4 = var1[var3];
                this.cp.getClass(dotToSlash(var4.getName()));
            }

            this.cp.setReadOnly();
            ByteArrayOutputStream var13 = new ByteArrayOutputStream();
            DataOutputStream var14 = new DataOutputStream(var13);

            try {
                var14.writeInt(-889275714);
                var14.writeShort(0);
                var14.writeShort(49);
                this.cp.write(var14);
                var14.writeShort(this.accessFlags);
                var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
                var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                var14.writeShort(this.interfaces.length);
                Class[] var17 = this.interfaces;
                int var18 = var17.length;

                for(int var19 = 0; var19 < var18; ++var19) {
                    Class var22 = var17[var19];
                    var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
                }

                var14.writeShort(this.fields.size());
                var15 = this.fields.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                    var20.write(var14);
                }

                var14.writeShort(this.methods.size());
                var15 = this.methods.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                    var21.write(var14);
                }

                var14.writeShort(0);
                return var13.toByteArray();
            } catch (IOException var9) {
                throw new InternalError("unexpected I/O Exception", var9);
            }
        }
    }

字節(jié)碼生成后,調(diào)用 defineClass0 來(lái)解析字節(jié)碼啊鸭,生成了 Proxy 的 Class 對(duì)象锹淌。在了解完代理類(lèi)動(dòng)態(tài)生成過(guò)程后,生產(chǎn)的代理類(lèi)是怎樣的赠制,誰(shuí)來(lái)執(zhí)行這個(gè)代理類(lèi)赂摆。

其中,在 ProxyGenerator.generateProxyClass 函數(shù)中 saveGeneratedFiles 定義如下钟些,其指代是否保存生成的代理類(lèi) class 文件烟号,默認(rèn) false 不保存。

在前面的示例中政恍,我們修改了此系統(tǒng)變量:

System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

動(dòng)態(tài)代理流程圖:

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

申明:開(kāi)始的圖片來(lái)源網(wǎng)絡(luò)汪拥,侵刪

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市篙耗,隨后出現(xiàn)的幾起案子喷楣,更是在濱河造成了極大的恐慌,老刑警劉巖鹤树,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铣焊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡罕伯,警方通過(guò)查閱死者的電腦和手機(jī)曲伊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)追他,“玉大人坟募,你說(shuō)我怎么就攤上這事∫乩辏” “怎么了懈糯?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)单雾。 經(jīng)常有香客問(wèn)我赚哗,道長(zhǎng)她紫,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任屿储,我火速辦了婚禮贿讹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘够掠。我一直安慰自己民褂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布疯潭。 她就那樣靜靜地躺著赊堪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪竖哩。 梳的紋絲不亂的頭發(fā)上哭廉,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音期丰,去河邊找鬼群叶。 笑死吃挑,一個(gè)胖子當(dāng)著我的面吹牛钝荡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播舶衬,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼埠通,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了逛犹?” 一聲冷哼從身側(cè)響起端辱,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎虽画,沒(méi)想到半個(gè)月后舞蔽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡码撰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年渗柿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脖岛。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡朵栖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出柴梆,到底是詐尸還是另有隱情陨溅,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布绍在,位于F島的核電站门扇,受9級(jí)特大地震影響雹有,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜悯嗓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一件舵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脯厨,春花似錦铅祸、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至稼跳,卻和暖如春盟庞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背汤善。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工什猖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人红淡。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓不狮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親在旱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子摇零,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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