Java動態(tài)代理詳解

2516326-5475e88a458a09e4.png

一,打破砂鍋問到底

  1. 什么事代理模式独榴?
  2. 什么是靜態(tài)代理僧叉,有啥缺陷?
  3. 什么是動態(tài)代理棺榔?
  4. JDK動態(tài)代理是如何動態(tài)生成類的瓶堕?
  5. 什么是cglib動態(tài)代理?有啥區(qū)別
  6. 動態(tài)代理是如何運(yùn)用在開發(fā)中的
Java動態(tài)代理詳解.png

二症歇,曉之以理郎笆,動之以碼

測試代碼:Github

1. 代理模式谭梗?

定義:
代理模式是給真實(shí)對象提供一個(gè)代理對象,并由代理對象控制對真實(shí)對象的引用宛蚓。
作用:
1.通過引入代理對象的方式來間接訪問真實(shí)對象激捏,防止直接訪問真實(shí)對象給系統(tǒng)帶來的不必要復(fù)雜性;
2.通過代理對象對原有的業(yè)務(wù)增強(qiáng)凄吏;

UML類圖:

代理模式UML.png

代理模式一般會有三個(gè)角色:
抽象對象: 指代理對象和真實(shí)對象對外提供的公共方法远舅,一般為一個(gè)接口。
真實(shí)對象: 需要實(shí)現(xiàn)抽象對象接口痕钢,定義了真實(shí)對象所要實(shí)現(xiàn)的業(yè)務(wù)邏輯图柏,以便供代理對象調(diào)用。也就是真正的業(yè)務(wù)邏輯在此任连。
代理對象: 需要實(shí)現(xiàn)抽象對象接口蚤吹,并包含真實(shí)對象的引用從而操作真實(shí)對象,是訪問者與真實(shí)對象之間的代理随抠,并可以附加自己的操作裁着。將統(tǒng)一的流程控制都放到代理對象中處理。

2. 靜態(tài)代理

靜態(tài)代理在使用時(shí),需要定義接口或者父類,真實(shí)對象與代理對象一起實(shí)現(xiàn)相同的抽象對象接口或者是繼承相同父類拱她。
一般來說二驰,被代理對象和代理對象是一對一的關(guān)系,當(dāng)然一個(gè)代理對象對應(yīng)多個(gè)被代理對象也是可以的秉沼。

代碼實(shí)現(xiàn):

   /**
     * 抽象角色:指代理角色和真實(shí)角色對外提供的公共方法诸蚕,一般為一個(gè)接口
     */
    public interface Subject {

        String request(String data);

        int response(int value);
    }
   /**
     * 真實(shí)角色:需要實(shí)現(xiàn)抽象角色接口,定義了真實(shí)角色所要實(shí)現(xiàn)的業(yè)務(wù)邏輯氧猬,以便供代理角色調(diào)用。也就是真正的業(yè)務(wù)邏輯在此坏瘩。
     */
    public static class RealSubject implements Subject, Subject2 {

        @Override
        public String request(String data) {
            System.out.println("RealSubject-data=" + data);
            return data;
        }

        @Override
        public int response(int value) {
            return value;
        }
    }
   /**
     * 代理角色:需要實(shí)現(xiàn)抽象角色接口盅抚,是真實(shí)角色的代理,通過真實(shí)角色的業(yè)務(wù)邏輯方法來實(shí)現(xiàn)抽象方法倔矾,并可以附加自己的操作妄均。將統(tǒng)一的流程控制都放到代理角色中處理!
     */
    public static class ProxySubject implements Subject {

        private Subject realSubject;

        public ProxySubject(Subject realSubject) {
            this.realSubject = realSubject;
        }

        @Override
        public String request(String data) {
            doSthBefore();
            realSubject.request(data);
            doSthAfter();
            return data;
        }

        @Override
        public int response(int value) {
            return value;
        }

        /**
         * 前置處理器
         */
        private void doSthAfter() {
            System.out.println("調(diào)用真實(shí)對象之后");
        }

        /**
         * 后置處理器
         */
        private void doSthBefore() {
            System.out.println("調(diào)用真實(shí)對象之前");
        }

    }

測試代碼:

   /**
     * Demo1:靜態(tài)代理的實(shí)現(xiàn)
     * 靜態(tài)代理在使用時(shí),需要定義接口或者父類,被代理對象與代理對象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類哪自。
     * 一般來說丰包,被代理對象和代理對象是一對一的關(guān)系,當(dāng)然一個(gè)代理對象對應(yīng)多個(gè)被代理對象也是可以的壤巷。
     * 靜態(tài)代理邑彪,一對一則會出現(xiàn)時(shí)靜態(tài)代理對象量多、代碼量大胧华,從而導(dǎo)致代碼復(fù)雜寄症,可維護(hù)性差的問題宙彪,
     * 一對多則代理對象會出現(xiàn)擴(kuò)展能力差的問題。
     */
    private static void Demo1() {
        System.out.println("-----Demo1-----\n\n");
        System.out.println("-----靜態(tài)代理的實(shí)現(xiàn)-----");
        Subject realSubject = new RealSubject();
        Subject proxySubject = new ProxySubject(realSubject);
        proxySubject.request("靜態(tài)代理類調(diào)用了");

    }

運(yùn)行結(jié)果:

-----Demo1-----


-----靜態(tài)代理的實(shí)現(xiàn)-----
調(diào)用真實(shí)對象之前
RealSubject-data=靜態(tài)代理類調(diào)用了
調(diào)用真實(shí)對象之后

Process finished with exit code 0

靜態(tài)代理的缺點(diǎn):
違反開閉原則 導(dǎo)致擴(kuò)展和可維護(hù)性差:

  1. 一對一則會出現(xiàn)時(shí)靜態(tài)代理對象量多有巧、代碼量大释漆,從而導(dǎo)致代碼復(fù)雜,可維護(hù)性差的問題篮迎;
  2. 一對多容易導(dǎo)致代理對象會出現(xiàn)擴(kuò)展能力差的問題男图。

3. 動態(tài)代理

是指在使用時(shí)再創(chuàng)建代理類和實(shí)例
在java的動態(tài)代理機(jī)制中,有兩個(gè)重要的類或接口甜橱,一個(gè)是InvocationHandler接口逊笆、另一個(gè)則是 Proxy類,這個(gè)類和接口是實(shí)現(xiàn)我們動態(tài)代理所必須用到的渗鬼。

Proxy
Proxy是用來創(chuàng)建動態(tài)代理對象的實(shí)例對象的览露,只要得到了這個(gè)對象我們就能調(diào)用到真實(shí)對象的方法。
InvocationHandler
InvocationHandler接口是給動態(tài)代理對象實(shí)現(xiàn)的譬胎,負(fù)責(zé)處理真實(shí)對象的操作的差牛,

代碼實(shí)現(xiàn):


    /**
     * 動態(tài)代理生成工廠類
     */
    public static class DynamicProxySubjectFactory implements InvocationHandler {

        /**
         * 持有的真實(shí)對象
         */
        private Subject realSubject;

        public DynamicProxySubjectFactory(Subject realSubject) {
            this.realSubject = realSubject;
        }

        /**
         * 通過Proxy獲得動態(tài)代理對象
         */
        public Object getProxyInstance() {
            System.out.println("---------------------getProxyInstance-----------------------");
            /**
             * Returns an instance of a proxy class for the specified interfaces * that dispatches method
             * invocations to the specified invocation handler. 返回一個(gè)實(shí)現(xiàn)了抽象接口的代理對象,該實(shí)例將方法調(diào)用委托給invocation
             * handler來調(diào)用堰乔。
             *
             * @param loader the class loader to define the proxy class loader 類加載器用于定義代理類
             * @param interfaces the list of interfaces for the proxy class to implement interfaces
             *        代理類要實(shí)現(xiàn)的接口列表
             * @param h the invocation handler to dispatch method invocations to
             *        InvocationHandler委托調(diào)用代理對象的方法
             * @return a proxy instance with the specified invocation handler of a * proxy class that is
             *        defined by the specified class loader * and that implements the specified interfaces
             *        返回一個(gè)具有包含InvocationHandler的并由指定類加載器定義且實(shí)現(xiàn)了指定接口的代理對象
             */
            System.out.println("---getProxyInstance-realSubject.getClass()=");
            System.out.println(realSubject.getClass());//class com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject
            System.out.println("---getProxyInstance-realSubject.getClass().getClassLoader()=");
            System.out.println(realSubject.getClass().getClassLoader());//sun.misc.Launcher$AppClassLoader@18b4aac2
            System.out.println("---getProxyInstance-realSubject.getClass().getInterfaces()[0]=");
            for (Class<?> anInterface : realSubject.getClass().getInterfaces()) {
                System.out.println(anInterface);//interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject
            }


            //獲取代理對象
            Object proxyInstance = Proxy.newProxyInstance(
                    realSubject.getClass().getClassLoader(),
                    realSubject.getClass().getInterfaces(),
                    this);
            System.out.println("---getProxyInstance-代理對象proxyInstance=");
            System.out.println(proxyInstance.getClass().getName());//com.sun.proxy.$Proxy0
            return proxyInstance;
        }

        /**
         * Processes a method invocation on a proxy instance and returns * the result. This method will
         * be invoked on an invocation handler * when a method is invoked on a proxy instance that it is
         * * associated with.
         *
         * 對代理對象執(zhí)行方法調(diào)用并返回結(jié)果偏化。 該方法會被和代理對象關(guān)聯(lián)對InvocationHandler接口對象調(diào)用。 如:在代理對象中request方法
         * super.h.invoke(this, m3, new Object[]{var1});
         *
         * @param proxy  調(diào)用該方法對代理對象
         * @param method the {@code Method} instance corresponding to * the interface method invoked on
         *               the proxy instance. The declaring * class of the {@code Method} object will be the
         *               interface that * the method was declared in, which may be a superinterface of the * proxy
         *               interface that the proxy class inherits the method through.
         *               與在代理對象上調(diào)用的接口方法相對應(yīng)的Method實(shí)例镐侯,Method實(shí)例是在聲明該方法的接口中被聲明對侦讨,這個(gè)接口也可能是代理類實(shí)現(xiàn)對代理接口的超接口
         * @param args   an array of objects containing the values of the * arguments passed in the method
         *               invocation on the proxy instance, * or {@code null} if interface method takes no
         *               arguments. * Arguments of primitive types are wrapped in instances of the * appropriate
         *               primitive wrapper class, such as * {@code java.lang.Integer} or {@code
         *               java.lang.Boolean}. 代理對象調(diào)用真實(shí)對象的方法所需要傳遞的參數(shù)值的對象數(shù)組;如果接口方法不接受參數(shù)苟翻,則為null韵卤。
         *               基本類型的參數(shù)將被自動裝箱為包裝類型,例如Integer崇猫,Boolean沈条。
         * @return the value to return from the method invocation on the * proxy instance. If the
         * declared return type of the interface * method is a primitive type, then the value
         * returned by * this method must be an instance of the corresponding primitive * wrapper
         * class; otherwise, it must be a type assignable to the * declared return type. If the
         * value returned by this method is * {@code null} and the interface method's return type is
         * * primitive, then a {@code NullPointerException} will be * thrown by the method
         * invocation on the proxy instance. If the * value returned by this method is otherwise not
         * compatible with * the interface method's declared return type as described above, * a
         * {@code ClassCastException} will be thrown by the method * invocation on the proxy
         * instance.
         * 代理實(shí)例上的方法調(diào)用返回的值。 如果接口方法的聲明的返回類型是原始類型诅炉,則此方法返回的值必須是對應(yīng)的原始包裝器類的實(shí)例蜡歹;
         * 否則,它必須是可分配給聲明的返回類型的類型涕烧。
         * 如果此方法返回的值為 null月而,并且接口方法的返回類型為原始類型,則在代理實(shí)例上的method調(diào)用將拋出NullPointerException议纯。
         * 如果此方法返回的*值與上述接口方法的聲明的返回類型不兼容父款,則proxyinstance上的調(diào)用方法引發(fā)ClassCastException。
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // super.h.invoke(this, m3, new Object[]{var1});
            System.out.println("---------------------invoke-----------------------");

            System.out.println("---invoke-代理對象實(shí)例proxy=" + proxy.getClass().getSimpleName());//invoke-proxy=$Proxy0
            System.out.println("---invoke-執(zhí)行的方法method=" + method.getName());//invoke-method=request
            for (Object arg : args) {
                System.out.println("---invoke-方法參數(shù)值args[i]=");
                System.out.println(arg);//動態(tài)代理類調(diào)用了
                System.out.println("---invoke-方法參數(shù)類型args[i].getClass().getSimpleName()=");
                System.out.println(arg.getClass().getSimpleName());//String
            }

            //通過動態(tài)代理對象方法進(jìn)行增強(qiáng)
            doSthAfter();
            //反射執(zhí)行真實(shí)對象對應(yīng)的method
            Object result = method.invoke(realSubject, args);
            doSthBefore();
            System.out.println("---invoke-方法返回值result=" + result);//result=動態(tài)代理類調(diào)用了
            return result;
        }

        /**
         * 前置處理
         */
        private void doSthAfter() {
            System.out.println("調(diào)用真實(shí)對象之后");
        }

        /**
         * 后置處理
         */
        private void doSthBefore() {
            System.out.println("調(diào)用真實(shí)對象之前");
        }

    }

測試代碼:

   /**
     * Demo2:JDK動態(tài)代理的實(shí)現(xiàn)
     * <p>
     * 動態(tài)代理是指在使用時(shí)再創(chuàng)建代理類和實(shí)例
     * 優(yōu)點(diǎn)
     * 只需要1個(gè)動態(tài)代理類就可以解決創(chuàng)建多個(gè)靜態(tài)代理的問題,避免重復(fù)铛漓、多余代碼更強(qiáng)的靈活性
     * 缺點(diǎn)
     * 效率低溯香,相比靜態(tài)代理中 直接調(diào)用目標(biāo)對象方法,動態(tài)代理則需要先通過Java反射機(jī)制 從而 間接調(diào)用目標(biāo)對象方法
     * 應(yīng)用場景局限浓恶,因?yàn)?Java 的單繼承特性(每個(gè)代理類都繼承了 Proxy 類)玫坛,即只能針對接口 創(chuàng)建 代理類,不能針對類創(chuàng)建代理類包晰。
     * 在java的動態(tài)代理機(jī)制中湿镀,有兩個(gè)重要的類或接口,一個(gè)是InvocationHandler接口伐憾、另一個(gè)則是 Proxy類勉痴,這個(gè)類和接口是實(shí)現(xiàn)我們動態(tài)代理所必須用到的。
     * InvocationHandler接口是給動態(tài)代理類實(shí)現(xiàn)的树肃,負(fù)責(zé)處理被代理對象的操作的蒸矛,
     * Proxy是用來創(chuàng)建動態(tài)代理類實(shí)例對象的,因?yàn)橹挥械玫搅诉@個(gè)對象我們才能調(diào)用那些需要代理的方法胸嘴。
     */
    private static void Demo2() {
        System.out.println("-----Demo2-----\n\n");
        System.out.println("-----JDK動態(tài)代理的實(shí)現(xiàn)-----");
        //創(chuàng)建真實(shí)對象實(shí)例
        Subject realSubject = new RealSubject();
        //將真實(shí)對象傳入代理生產(chǎn)工廠類中
        DynamicProxySubjectFactory proxySubjectFactory = new DynamicProxySubjectFactory(realSubject);
        //獲取jdk動態(tài)生成的代理類
        Subject proxySubject = (Subject) proxySubjectFactory.getProxyInstance();
        //執(zhí)行真實(shí)對象的方法
        proxySubject.request("動態(tài)代理類調(diào)用了");
        proxySubject.response(521);
    }

運(yùn)行結(jié)果:

-----Demo2-----


-----JDK動態(tài)代理的實(shí)現(xiàn)-----
---------------------getProxyInstance-----------------------
---getProxyInstance-realSubject.getClass()=
class com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject
---getProxyInstance-realSubject.getClass().getClassLoader()=
sun.misc.Launcher$AppClassLoader@18b4aac2
---getProxyInstance-realSubject.getClass().getInterfaces()[0]=
interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject
interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject2
---getProxyInstance-代理對象proxyInstance=
com.sun.proxy.$Proxy0
---------------------invoke-----------------------
---invoke-代理對象實(shí)例proxy=$Proxy0
---invoke-執(zhí)行的方法method=request
---invoke-方法參數(shù)值args[i]=
動態(tài)代理類調(diào)用了
---invoke-方法參數(shù)類型args[i].getClass().getSimpleName()=
String
調(diào)用真實(shí)對象之后
RealSubject-data=動態(tài)代理類調(diào)用了
調(diào)用真實(shí)對象之前
---invoke-方法返回值result=動態(tài)代理類調(diào)用了
---------------------invoke-----------------------
---invoke-代理對象實(shí)例proxy=$Proxy0
---invoke-執(zhí)行的方法method=response
---invoke-方法參數(shù)值args[i]=
521
---invoke-方法參數(shù)類型args[i].getClass().getSimpleName()=
Integer
調(diào)用真實(shí)對象之后
調(diào)用真實(shí)對象之前
---invoke-方法返回值result=521

Process finished with exit code 0

優(yōu)點(diǎn):
只需要1個(gè)動態(tài)代理類就可以解決創(chuàng)建多個(gè)靜態(tài)代理的問題雏掠,避免重復(fù)、多余代碼劣像,有更強(qiáng)的靈活性乡话。
缺點(diǎn):
效率低:相比靜態(tài)代理中 直接調(diào)用目標(biāo)對象方法,動態(tài)代理則需要先通過Java反射機(jī)制從而間接調(diào)用目標(biāo)對象方法
應(yīng)用場景局限:因?yàn)镴ava的單繼承特性(每個(gè)代理類都繼承了Proxy類)耳奕,即只能針對接口(多實(shí)現(xiàn))創(chuàng)建代理類绑青,不能針對類創(chuàng)建代理類。

4. JDK動態(tài)代理源碼解析

類的完整加載生命周期

  1. Java源文件-編譯
  2. Java字節(jié)碼文件-類加載(來源:磁盤或者內(nèi)存)
  3. Class對象-實(shí)例化
  4. 實(shí)例對象
  5. 卸載


    類加載的完整生命周期.png

通過調(diào)試模式我們發(fā)現(xiàn)屋群,動態(tài)代理里闸婴,代理類的類名是這樣的:
com.sun.proxy.$Proxy0

這個(gè)代理類為何是這個(gè)名字?它是如何執(zhí)行真實(shí)對象的相關(guān)方法呢芍躏?我們在java文件編譯后的目錄里其實(shí)找不到這個(gè)名為$Proxy0的class文件的掠拳。
觀察源碼的執(zhí)行流程發(fā)現(xiàn),這個(gè)動態(tài)代理的字節(jié)碼文件是在內(nèi)存中創(chuàng)建的纸肉,與創(chuàng)建對象有關(guān)的代碼主要有:

com/jay/java/DynamicProxy/test/ProxyMainTest.java.Demo2()

//創(chuàng)建真實(shí)對象實(shí)例
Subject realSubject = new RealSubject();
//將真實(shí)對象傳入代理生產(chǎn)工廠類中
DynamicProxySubjectFactory proxySubjectFactory = new DynamicProxySubjectFactory(realSubject);
//獲取jdk動態(tài)生成的代理類
Subject proxySubject = (Subject) proxySubjectFactory.getProxyInstance();

com/jay/java/DynamicProxy/test/ProxyMainTest.DynamicProxySubjectFactory.getProxyInstance()

//獲取代理對象
Object proxyInstance = Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(),this);
return proxyInstance;

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/src.zip!/java/lang/reflect/Proxy.java.newProxyInstance()

//查找或生成指定的代理類。
Class<?> cl = getProxyClass0(loader, intfs);
//代理類構(gòu)造函數(shù)的參數(shù)類型
private static final Class<?>[] constructorParams = { InvocationHandler.class };
//獲取代理對象構(gòu)造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
//返回代理對象
return cons.newInstance(new Object[]{h});

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/src.zip!/java/lang/reflect/Proxy.java.getProxyClass0()

//代理類的緩存
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory
//如果代理類是由實(shí)現(xiàn)的加載器定義的
//給定的接口存在喊熟,這將簡單地返回緩存的副本;
//否則柏肪,它將通過ProxyClassFactory創(chuàng)建代理類
return proxyClassCache.get(loader, interfaces);

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/src.zip!/java/lang/reflect/WeakCache.java.get()

//創(chuàng)建subKey并檢索可用的的Supplier<V>存儲
//subKey來自valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/src.zip!/java/lang/reflect/Proxy.ProxyClassFactory.java.apply()

//為要生成的代理類選擇一個(gè)名稱。
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//生成指定的代理類芥牌。
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
//通過生成的字節(jié)碼文件生成動態(tài)代理類
return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
//native 代碼暫時(shí)不能向下追蹤
private static native Class<?> defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/rt.jar!/sun/misc/ProxyGenerator.class.generateProxyClass()

//由IntelliJ IDEA從.class文件重新創(chuàng)建的源代碼
//(由Fernflower反編譯器提供動力)

com/jay/java/DynamicProxy/test/ProxyMainTest.java.Demo2()

//執(zhí)行代理對象的方法
proxySubject.request("動態(tài)代理類調(diào)用了");

com.sun.proxy.$Proxy0.request()

//通過InvocationHandler接口回掉出來代理對象的方法調(diào)用
super.h.invoke(this, m3, new Object[]{var1});

com/jay/java/DynamicProxy/test/ProxyMainTest.DynamicProxySubjectFactory.invoke()

//反射執(zhí)行真實(shí)對象對應(yīng)的method
Object result = method.invoke(realSubject, args);
return result;

以上就是創(chuàng)建代理對象以及代理對象執(zhí)行真實(shí)對的的方法的大體流程
雖然我們無法追溯到代理類的字節(jié)碼是如何生成的烦味,但是我們可以取巧利用jdk源碼將字節(jié)碼通過文件流輸出到本地,方法如下:

   /**
     * 模擬動態(tài)生成代理類并輸出到指定路徑,路徑不能包含中文字符
     * /DevelopNote/Java/build/classes/java/main/com/jay/java/DynamicProxy/$Proxy0.class
     */
    private static void generateProxyClassFile(Class clazz, String proxyName) {
        /*ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);*/
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, new Class[]{clazz});
        String paths = clazz.getResource(".").getPath();
        System.out.println("生成地址:" + paths);
        System.out.println("代理類命:" + proxyName);
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(paths + proxyName + ".class");
            out.write(proxyClassFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

最終我們可以這個(gè)看到這個(gè)神秘的Proxy0.class里面的代碼如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.jay.java.DynamicProxy.test.ProxyMainTest.RealSubject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements RealSubject {
    private static Method m1;
    private static Method m9;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m6;
    private static Method m5;
    private static Method m8;
    private static Method m10;
    private static Method m0;
    private static Method m7;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void notify() throws  {
        try {
            super.h.invoke(this, m9, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String request(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int response(int var1) throws  {
        try {
            return (Integer)super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void wait(long var1) throws InterruptedException {
        try {
            super.h.invoke(this, m6, new Object[]{var1});
        } catch (RuntimeException | InterruptedException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    public final void wait(long var1, int var3) throws InterruptedException {
        try {
            super.h.invoke(this, m5, new Object[]{var1, var3});
        } catch (RuntimeException | InterruptedException | Error var5) {
            throw var5;
        } catch (Throwable var6) {
            throw new UndeclaredThrowableException(var6);
        }
    }

    public final Class getClass() throws  {
        try {
            return (Class)super.h.invoke(this, m8, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void notifyAll() throws  {
        try {
            super.h.invoke(this, m10, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void wait() throws InterruptedException {
        try {
            super.h.invoke(this, m7, (Object[])null);
        } catch (RuntimeException | InterruptedException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m9 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("notify");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("request", Class.forName("java.lang.String"));
            m4 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("response", Integer.TYPE);
            m6 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("wait", Long.TYPE);
            m5 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("wait", Long.TYPE, Integer.TYPE);
            m8 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("getClass");
            m10 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("notifyAll");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m7 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("wait");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

測試代碼:

  /**
     * Demo3:JDK動態(tài)代理原理分析
     * 通過調(diào)試模式我們發(fā)現(xiàn)谬俄,動態(tài)代理里柏靶,代理類的類名是這樣的:
     * {$Proxy0@505}com.jay.java.動態(tài)代理.ProxyMainTest$RealSubject@3c679bde
     * 這個(gè)代理類為何是這個(gè)名字?它是如何執(zhí)行被代理對象的相關(guān)方法呢溃论?我們在java文件編譯后的目錄里其實(shí)找不到這個(gè)名為$Proxy0的class文件的屎蜓。
     * 觀察Proxy.newProxyInstance方法,與創(chuàng)建對象有關(guān)的代碼主要有:
     * 1,獲得代理類的class對象:
     * Class<?> cl = getProxyClass0(loader, intfs);
     * 2,獲得代理類的構(gòu)造器:
     * final Constructor<?> cons = cl.getConstructor(constructorParams);
     * 3,創(chuàng)建代理類的實(shí)例
     * return cons.newInstance(new Object[]{h});
     * <p>
     * 看來其中的關(guān)鍵點(diǎn)就是如何獲得代理類的class對象钥勋,我們進(jìn)入getProxyClass0方法炬转,進(jìn)而進(jìn)入proxyClassCache.get方法,通過這個(gè)這個(gè)方法所在的類名算灸,我們可以推測扼劈,JDK內(nèi)部使用了某種機(jī)制緩存了我們的代理類的class對象,同時(shí)get方法接受的參數(shù)是被代理類的類加載器和類實(shí)現(xiàn)的的接口菲驴。
     * 在這個(gè)get方法中荐吵,除去和緩存相關(guān)的操作,同時(shí)用到了被代理類的類加載器和類實(shí)現(xiàn)的的接口這兩個(gè)參數(shù)的是
     * Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
     * Generate the specified proxy class.
     * byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,interfaces,accessFlags);
     * String proxyName = proxyPkg + proxyClassNamePrefix + num;
     * return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
     * 而最終生成代理類的class對象是defineClass0方法赊瞬,但是這個(gè)方法是個(gè)native方法先煎,所以我們不去也無法深究它,但是通過這個(gè)方法的參數(shù)我們可以明顯看到它接收了上面所生成的byte數(shù)組森逮。
     * private static native Class<?> defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);
     */
    private static void Demo3() {
        System.out.println("-----Demo3-----\n\n");
        System.out.println("-----JDK動態(tài)代理原理分析-----");
        Subject realSubject = new RealSubject();
        //將被代理對象傳入代理生成工廠類
        DynamicProxySubjectFactory proxySubjectFactory = new DynamicProxySubjectFactory(realSubject);
        //獲取jdk動態(tài)生成的代理類
        Subject proxySubject = (Subject) proxySubjectFactory.getProxyInstance();
        //模擬動態(tài)生成代理類字節(jié)碼文件并輸出到本地榨婆,
        generateProxyClassFile(realSubject.getClass(), proxySubject.getClass().getSimpleName());
        //獲取代理類的方法
        Method[] methods = proxySubject.getClass().getMethods();
        //打印方法名稱
        for (Method method : methods) {
            System.out.println(method.getName());
        }
    }

運(yùn)行結(jié)果:

-----Demo3-----


-----JDK動態(tài)代理原理分析-----
---------------------getProxyInstance-----------------------
---getProxyInstance-realSubject.getClass()=
class com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject
---getProxyInstance-realSubject.getClass().getClassLoader()=
sun.misc.Launcher$AppClassLoader@18b4aac2
---getProxyInstance-realSubject.getClass().getInterfaces()[0]=
interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject
interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject2
---getProxyInstance-代理對象proxyInstance=
com.sun.proxy.$Proxy0
生成地址:/Users/xuejiewang/AndroidStudioProjects/Jay/DevelopNote/Java/build/classes/java/main/com/jay/java/DynamicProxy/test/
代理類命:$Proxy0
equals
toString
hashCode
request
response
isProxyClass
newProxyInstance
getProxyClass
getInvocationHandler
wait
wait
wait
getClass
notify
notifyAll

Process finished with exit code 0

4. cglib動態(tài)代理

  • jdk動態(tài)代理:代理所有“實(shí)現(xiàn)抽象對象接口接口”的真實(shí)對象
  • cglib動態(tài)代理:代理任意一個(gè)目標(biāo)類,但對final類和方法無法代理
  • 不同點(diǎn):jdk動態(tài)代理的目標(biāo)類必須實(shí)現(xiàn)的有接口,因?yàn)樵谡{(diào)用Proxy.newProxyInstance()的時(shí)候需要傳入目標(biāo)類的接口類褒侧。而cglib不做此限制良风。

三,實(shí)踐出真知

測試代碼:Github

1. Retrofit中對動態(tài)代理的使用

Retrofit的一般使用如下:

   /**
     * 數(shù)據(jù)接受實(shí)體類
     */
    private class Repo {
        private String id;
        private String name;
        private String description;
        private String url;

        @Override
        public String toString() {
            return "Repo{" +
                    "id='" + id + '\'' +
                    ", name='" + name + '\'' +
                    ", description='" + description + '\'' +
                    ", url='" + url + '\'' +
                    '}';
        }
    }
 /**
     * Retrofit將您的HTTP API轉(zhuǎn)換為Java接口闷供。
     * 抽象接口對象
     */
    public interface GitHubService {
        //https://api.github.com/users/Jay-Droid/repos
        @GET("users/{user}/repos")
        Call<List<Repo>> listRepos(@Path("user") String user);
    }

測試代碼:

  /**
     * Demo1:Retrofit中對動態(tài)代理的使用
     * https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit2/Retrofit.java
     * <p>
     * retrofit.create(GitHubService.class);
     */
    private static void Demo1() {
        System.out.println("-----Demo1-----\n\n");
        System.out.println("-----Retrofit中對動態(tài)代理的使用-----");
        //Retrofit類生成GitHubService接口的實(shí)現(xiàn)烟央。
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        GitHubService service = retrofit.create(GitHubService.class);
        //從創(chuàng)建的GitHubService進(jìn)行的每次調(diào)用都可以向遠(yuǎn)程Web服務(wù)器發(fā)出同步或異步HTTP請求。
        Call<List<Repo>> repos = service.listRepos("Jay-Droid");
        repos.enqueue(new Callback<List<Repo>>() {
            @Override
            public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
                System.out.println("-----onResponse-----");
                //Repo{id='76014709', name='ActivityOptions', description='android ActivityOption 中的五種轉(zhuǎn)場動畫', url='https://api.github.com/repos/Jay-Droid/ActivityOptions'}
                System.out.println(response.body().get(0).toString());
            }

            @Override
            public void onFailure(Call<List<Repo>> call, Throwable throwable) {
                System.out.println("-----onFailure-----");
                System.out.println(throwable.getLocalizedMessage());
            }
        });
    }

運(yùn)行結(jié)果:

-----Demo1-----


-----Retrofit中對動態(tài)代理的使用-----
-----onResponse-----
Repo{id='76014709', name='ActivityOptions', description='android ActivityOption 中的五種轉(zhuǎn)場動畫', url='https://api.github.com/repos/Jay-Droid/ActivityOptions'}

Process finished with exit code 0

        /**
         * 利用動態(tài)代理技術(shù)生成代理對象
         * retrofit.create()
         */
        public <T> T create(final Class<T> service) {
            Utils.validateServiceInterface(service);
            if (validateEagerly) {
                eagerlyValidateMethods(service);
            }
            return (T) Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[] { service },
              InvocationHandler內(nèi)部實(shí)現(xiàn)類
            new InvocationHandler() {
                        private final Platform platform = Platform.get();
                        private final Object[] emptyArgs = new Object[0];
                        @Override public @Nullable Object invoke(Object proxy, Method method,
                                                                 @Nullable Object[] args) throws Throwable {
                            // 如果方法是來自對象的方法歪脏,則不做代理
                            if (method.getDeclaringClass() == Object.class) {
                                return method.invoke(this, args);
                            }
                               判斷是否是默認(rèn)方法疑俭,這是1.8新增的內(nèi)容
                            if (platform.isDefaultMethod(method)) {
                                return platform.invokeDefaultMethod(method, service, proxy, args);
                            }
                            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
                        }
                    });
        }

2. 動態(tài)代理在組件化路由框架中的實(shí)踐

  /**
   * Demo2:動態(tài)代理組件化路由框架實(shí)戰(zhàn) 關(guān)鍵詞:URI,Scheme婿失,注解钞艇,動態(tài)代理
   * 詳見:com.jay.develop.java.dynamic_proxy.DynamicProxyActivity 實(shí)現(xiàn)步驟:
   * 1.定義兩個(gè)注解參數(shù),一個(gè)標(biāo)示URI注解豪硅,一個(gè)標(biāo)示參數(shù) @Documented @Target(METHOD) @Retention(RUNTIME) public @interface
   * RouterUri { String routerUri() default ""; } @Documented @Target(PARAMETER) @Retention(RUNTIME)
   * public @interface RouterParam { String value() default "";
   *
   * <p>}
   */
  private static void Demo2() {
        System.out.println("-----Demo2-----\n\n");
        System.out.println("-----動態(tài)代理組件化路由框架實(shí)戰(zhàn) -----");
    /**
     * 1.定義兩個(gè)注解參數(shù)哩照,一個(gè)標(biāo)示URI注解,一個(gè)標(biāo)示參數(shù)
     *
      //請求Url地址,在AndroidManifest文件中為每個(gè)Activity添加scheme
      @RouterUri(routerUri = "dv://com.jay.develop/router")
      void jumpToDynamicProxyPage(
              @RouterParam("info") String info,
              @RouterParam("des") String des
    );

      @Documented
      @Target(PARAMETER)
      @Retention(RUNTIME)
      public @interface RouterParam {
          String value() default "";

      }

      2.定義一個(gè)URI協(xié)議接口
      public interface IRouterUri {
      //請求Url地址,在AndroidManifest文件中為每個(gè)Activity添加scheme
     @RouterUri(routerUri = "dv://com.jay.develop/router")
     void jumpToDynamicProxyPage(
         @RouterParam("info") String info,

         @RouterParam("des") String des
         );
     }

      3.定義一個(gè)單例懒浮,內(nèi)部通過動態(tài)代理機(jī)制實(shí)現(xiàn)跳轉(zhuǎn)

         XLRouter.class

      4.)在進(jìn)行XLRouter初始化

         XLRouter.initXLRouter(this);

      5.調(diào)用方式

         XLRouter.routerUri().jumpToGoodsDetail("1000110002","goods des");

      **/

    }

具體實(shí)現(xiàn):

/**
 * 注解參數(shù),標(biāo)示參數(shù)注解
 * 
 * @author wangxuejie
 * @version 1.0
 * @date 2019-12-02 18:19
 */
@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface RouterParam {
    String value() default "";
}
/**
 * 注解參數(shù),標(biāo)示URI注解
 * @author wangxuejie
 * @version 1.0
 * @date 2019-12-02 18:20
 */
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface RouterUri {

    String routerUri() default "";

}
/**
 * 定義一個(gè)URI協(xié)議接口
 *
 * @author wangxuejie
 * @version 1.0
 * @date 2019-12-02 18:18
 */
public interface IRouterUri {
    //請求Url地址,在AndroidManifest文件中為每個(gè)Activity添加scheme
    @RouterUri(routerUri = "dv://com.jay.develop/router")
    void jumpToDynamicProxyPage(
            @RouterParam("info") String info,

            @RouterParam("des") String des
    );
}
/**
 * 內(nèi)部通過動態(tài)代理機(jī)制實(shí)現(xiàn)跳轉(zhuǎn)
 * <p>
 * 參考地址: https://www.cnblogs.com/whoislcj/p/5860138.html
 *
 * @author wangxuejie
 * @version 1.0
 * @date 2019-12-02 18:15
 */
public class XLRouter {
    private final static String TAG = XLRouter.class.getSimpleName();
    private static IRouterUri mRouterUri;
    private static Context mContext;

    /**
     * 初始化
     */
    public static void initXLRouter(Context context) {
        mContext = context.getApplicationContext();
        mRouterUri = create(IRouterUri.class);
    }

    /**
     * 返回Api
     */
    public static IRouterUri routerUri() {
        if (mRouterUri == null) {
            throw new IllegalStateException("XLRouter還未初始化");
        }
        return mRouterUri;
    }

    /**
     * 通過動態(tài)代理生成IRouterUri接口的實(shí)例對象并解析注解參數(shù)
     *
     * @param aClass IRouterUri Class對象
     * @return 真實(shí)對象的代理對象
     */
    private static IRouterUri create(Class aClass) {
        return (IRouterUri) Proxy.newProxyInstance(aClass.getClassLoader(), new Class[]{aClass},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object... args) throws Throwable {
                        StringBuilder stringBuilder = new StringBuilder();
                        //獲取RouterUri注解
                        RouterUri reqUrl = method.getAnnotation(RouterUri.class);
                        Log.e(TAG, "IReqApi---reqUrl->" + reqUrl.routerUri());
                        stringBuilder.append(reqUrl.routerUri());
                        //Type[] parameterTypes = method.getGenericParameterTypes();//獲取注解參數(shù)類型
                        //拿到參數(shù)注解
                        Annotation[][] parameterAnnotationsArray = method.getParameterAnnotations();
                        //Annotation[] annotation = method.getDeclaredAnnotations();
                        /*
                            定義的路由接口
                            @RouterUri(routerUri = "dv://com.jay.develop/router")
                                void jumpToDynamicProxyPage(
                                        @RouterParam("info") String info,
                                        @RouterParam("des") String des
                             );
                         */
                        int pos = 0;
                        for (int i = 0; i < parameterAnnotationsArray.length; i++) {
                            Annotation[] annotations = parameterAnnotationsArray[i];
                            if (annotations != null && annotations.length != 0) {
                                if (pos == 0) {
                                    stringBuilder.append("?");
                                } else {
                                    stringBuilder.append("&");
                                }
                                pos++;
                                RouterParam reqParam = (RouterParam) annotations[0];
                                stringBuilder.append(reqParam.value());
                                stringBuilder.append("=");
                                stringBuilder.append(args[i]);
                                Log.e(TAG, "reqParam---reqParam->" + reqParam.value() + "=" + args[i]);
                            }
                        }
                        Log.e(TAG, "stringBuilder.toString()->" + stringBuilder.toString());
                        //dv://com.jay.develop/router?info=我是通過XLRouter路由框架跳轉(zhuǎn)的&des=描述
                        //下面就可以執(zhí)行相應(yīng)的跳轉(zhuǎn)操作
                        openRouterUri(stringBuilder.toString());
                        return null;
                    }
                });
    }

    /**
     * 通過uri跳轉(zhuǎn)指定頁面
     *
     * @param url deeplink
     */
    private static void openRouterUri(String url) {
        PackageManager packageManager = mContext.getPackageManager();
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        List activities = packageManager.queryIntentActivities(intent, 0);
        boolean isValid = !activities.isEmpty();
        if (isValid) {
            mContext.startActivity(intent);
        }
    }

    /**
     * 4.)在進(jìn)行XLRouter初始化
     *
     * XLRouter.initXLRouter(this);
     *
     *
     * 5.調(diào)用方式
     *
     * XLRouter.routerUri().jumpToGoodsDetail("1000110002","goods des");
     */
}

測試代碼:

//A Activity中調(diào)用
XLRouter.routerUri().jumpToDynamicProxyPage("我是通過XLRouter路由框架跳轉(zhuǎn)的", "我是描述");
//B Activity中注冊deeplink
 <activity android:name=".java.dynamic_proxy.DynamicProxyActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <!--dv://com.jay.develop/router-->
                <data
                        android:host="com.jay.develop"
                        android:path="/router"
                        android:scheme="dv" />

            </intent-filter>

        </activity>
/**
 * 動態(tài)代理組件化路由框架實(shí)戰(zhàn)
 * URI飘弧,Scheme,注解,動態(tài)代理
 * 通過該方法打開該頁面
 * XLRouter.routerUri().jumpToDynamicProxyPage("我是通過XLRouter路由框架跳轉(zhuǎn)的", "我是描述");
 */
class DynamicProxyActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_dynamic_proxy)
        getDataFromBrowser()
    }

    /**
     * 從deep link中獲取數(shù)據(jù)
     */
    private fun getDataFromBrowser() {
        val data = intent.data
        try {
            tv_info.text =
                "Uri  :" + data!!.toString() + "\n" +
                        "Scheme: " + data!!.scheme + "\n" +
                        "host: " + data!!.host + "\n" +
                        "path: " + data!!.path
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末次伶,一起剝皮案震驚了整個(gè)濱河市痴昧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌冠王,老刑警劉巖赶撰,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異版确,居然都是意外死亡扣囊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門绒疗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來侵歇,“玉大人,你說我怎么就攤上這事吓蘑√杪牵” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵磨镶,是天一觀的道長溃蔫。 經(jīng)常有香客問我,道長琳猫,這世上最難降的妖魔是什么伟叛? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮脐嫂,結(jié)果婚禮上统刮,老公的妹妹穿的比我還像新娘。我一直安慰自己账千,他們只是感情好侥蒙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著匀奏,像睡著了一般鞭衩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上娃善,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天论衍,我揣著相機(jī)與錄音,去河邊找鬼聚磺。 笑死饲齐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的咧最。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼矢沿!你這毒婦竟也來了们衙?” 一聲冷哼從身側(cè)響起赔桌,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后砂蔽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛛砰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年芍殖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片外厂。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡冕象,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出汁蝶,到底是詐尸還是另有隱情渐扮,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布掖棉,位于F島的核電站墓律,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏幔亥。R本人自食惡果不足惜耻讽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望帕棉。 院中可真熱鬧针肥,春花似錦、人聲如沸笤昨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瞒窒。三九已至捺僻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間崇裁,已是汗流浹背匕坯。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拔稳,地道東北人葛峻。 一個(gè)月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像巴比,于是被迫代替她去往敵國和親术奖。 傳聞我的和親對象是個(gè)殘疾皇子礁遵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353