搞定代理模式,看懂Retrofit動態(tài)代理的騷操作

代理模式

看完文章你能學(xué)到什么?搞懂代理模式矿微,Retrofit代理模式的使用(其實我就是因為沒看懂巡通,才學(xué)的),文章有點長但是邏輯很簡單

代理模式可以在不修改被代理對象的基礎(chǔ)上,通過擴展代理類礼搁,進行一些功能的附加與增強宙搬。 值得注意的是笨腥,代理類和被代理類應(yīng)該共同實現(xiàn)一個接口,或者是共同繼承某個類害淤。----Frank909 (并不是我總結(jié)的,但是我挺認(rèn)同的)

簡單來說就是:你朋友圈的小花賣面膜扇雕,面膜100塊一盒,但是經(jīng)過小花的代理變300了窥摄,此時面膜還是那個面膜镶奉,就是到手的時候總感覺有點貴!

靜態(tài)代理

把上面說所的崭放,用程序來表達:

定義一個產(chǎn)品的接口哨苛,產(chǎn)品的屬性是價格

public interface IProduct {
    void price();
}

創(chuàng)建面膜類實現(xiàn)產(chǎn)品接口

public class FacialMask implements IProduct {

    @Override
    public void price() {
        System.out.println("出廠價格100塊");
    }
}

最后創(chuàng)建一個代理類:小花

public class XiaoHuaProxy implements IProduct {

    private IProduct product;

    public XiaoHuaProxy(IProduct product) {
        this.product = product;
    }

    @Override
    public void price() {
        System.out.println("我是小fafa");
        product.price();
        System.out.println("面膜現(xiàn)在300塊,100不存在的");
    }
}

main 方法

FacialMask facialMask = new FacialMask();
XiaoHuaProxy xiaoHuaProxy = new XiaoHuaProxy(facialMask);
xiaoHuaProxy.price();

運行一下

我是小fafa
出廠價格100塊
面膜現(xiàn)在300塊币砂,100不存在的

然后這就是靜態(tài)代理建峭,因為代理類是靜態(tài)生成的,以上要知道的一點就是:

代理模式可以在不修改被代理對象的基礎(chǔ)上决摧,通過擴展代理類亿蒸,進行一些功能的附加與增強。

有人可能會杠一下掌桩,老子就不實現(xiàn)同一個接口躏救,同一個繼承對象提岔,照樣能實現(xiàn)领突,所以下面看下動態(tài)代理

動態(tài)代理

動態(tài)代理和靜態(tài)代理功能上是沒有任何區(qū)別的险耀,動態(tài)代理我的理解:動態(tài)生成代理類,完成代理的功能则拷。小花不再被顯示創(chuàng)建出來贡蓖,并不是面膜可以被小花代理曹鸠,那么也可以被小明代理,然后動態(tài)代理就是為了生成多個不同樣的代理對象斥铺。

但是在這之前要補充一個反射的小知識彻桃,才能更好的看下去

反射 Method 方法的執(zhí)行:

我們在應(yīng)用反射的時候往往是想執(zhí)行一些我們無法使用的類或者方法;
Method.invoke(Object obj, Object... args);

public Object invoke(Object obj, Object... args) {}

invoke() 方法中第一個參數(shù) Object 實質(zhì)上是 Method 所依附的 Class 對應(yīng)的類的實例仅父,如果這個方法是一個靜態(tài)方法叛薯,那么 ojb 為 null浑吟,后面的可變參數(shù) Object 對應(yīng)的自然就是參數(shù)笙纤。這里看不懂沒事,下面有例子组力;

舉個例子省容,F(xiàn)acialMask(面膜類)

//不使用反射,獲取對象和執(zhí)行方法
FacialMask facialMask = new FacialMask();
facialMask.price();
//使用反射實例化獲取類對象燎字,有很多種方式
FacialMask reflectMaxk = FacialMask.class.newInstance();
//不使用反射直接調(diào)用
reflectMaxk.price();
//突然發(fā)現(xiàn)price是個私有方法腥椒,可以通過反射執(zhí)行這個方法
Method price = FacialMask.class.getMethod("price");
price.setAccessible(true);//把類的可見性打開,即使是私有也能調(diào)用
//參數(shù)一候衍,就是要調(diào)用類的對象笼蛛,靜態(tài)方法可以傳null,參數(shù)二:調(diào)用方法的參數(shù),一定是要按順尋的蛉鹿,沒有可以不寫
 Object invoke = price.invoke(reflectMaxk, new Class[]{});//并且返回方法的返回值

了解這么多看下面這個文章就夠了滨砍,如果想深入了解推薦看《細(xì)說反射,Java 和 Android 開發(fā)者必須跨越的坎》


Proxy.newProxyInstance 生成代理類,完成代理的功能

下面用程序演示

public static void main(String[] args) {
       FacialMask facialMask = new FacialMask();
        IProduct proxyInstance = (IProduct) Proxy.newProxyInstance(FacialMask.class.getClassLoader(), new Class[]{IProduct.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("我是小fafa");
                Object invoke = method.invoke(facialMask, args);
                System.out.println("面膜現(xiàn)在300塊妖异,100不存在的");
                return invoke;
            }
        });
        proxyInstance.price();
    }

我們使用Proxy.newProxyInstance()取代了XiaoHuaProxy類惋戏,看下輸出結(jié)果

我是小fafa
出廠價格100塊
面膜現(xiàn)在300塊,100不存在的

源碼中是這樣描述這個類的他膳,返回一個指定接口的代理類實例响逢,這個實例的方法都會去調(diào)用InvocationHandler。

  • 參數(shù)一:ClassLoader 類加載器棕孙,這里可以傳代理類要實現(xiàn)的接口的ClassLoader

這里大致介紹下舔亭,類加載器默認(rèn)有三個:Bootstrap ClassLoader 最頂層的加載類;Extention ClassLoader 擴展的類加載器蟀俊;Appclass Loader也稱為SystemAppClass 加載當(dāng)前應(yīng)用的classpath的所有類钦铺,一般你寫的程序都是由這個類加載的,自定義類加載器默認(rèn)的父類(并不是繼承關(guān)系)是Appclass Loader
,詳細(xì)可以看: 《一看你就懂欧漱,超詳細(xì)java中的ClassLoader詳解》

  • 參數(shù)二:Class<?>[] 要實現(xiàn)的接口职抡,因為一個類可以實現(xiàn)多個接口,所以這里是個數(shù)組
  • 參數(shù)三:InvocationHandler 這個就是接口代理類執(zhí)行方法的回調(diào)
//源碼 jdk 8 基礎(chǔ)上
 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //刪除一些校驗參數(shù)合法的代碼

        /**
         * Look up or generate the designated proxy class.
         * 生成代理類或者查出已生成代理類
         */
         
        Class<?> cl = getProxyClass0(loader, intfs);

        /**
         * Invoke its constructor with the designated invocation handler.
         * 把代理類實例化误甚,返回去
         */
      
        //刪除一些try catch
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        //刪除一些校檢
        //注意這個地方缚甩,實例化代理類谱净,把== h ==當(dāng)參數(shù)傳進去了,這個== h ==就是咱們實現(xiàn)InvocationHandler的方法
        return cons.newInstance(new Object[]{h});
      
    }

獲取生成的字節(jié)碼文件

getProxyClass0()獲取代理類字節(jié)碼文件擅威,主要使用了ProxyGenerator.generateProxyClass()生成了代理類壕探,默認(rèn)在內(nèi)存中,也可以設(shè)置System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true") 獲取本地文件, 通過此語句可以立個flag讓生成字節(jié)碼文件的時候郊丛,輸出本地字節(jié)碼文件,注意這句話一定要在獲取代理類實例之前,
生成的文件在當(dāng)前項目的根目錄李请,對應(yīng)的包名文件夾里


 private static final boolean saveGeneratedFiles = 
 (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));//這就是那個flag的獲取

 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));
                            Files.createDirectories(var3);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class");
                        }

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

廢了這么大勁兒,看下動態(tài)生成的代理類字節(jié)碼文件吧厉熟!

public final class $Proxy0 extends Proxy implements IProduct {
   
   //刪除一些不重要的成員變量

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

    public final boolean equals(Object var1) throws  {
        //刪除了一些tryCatch
        return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
    }

    public final void price() throws  {
      //刪除了一些tryCatch,
      super.h.invoke(this, m3, (Object[])null);
    }

    public final String toString() throws  {
        //刪除了一些tryCatch
        return (String)super.h.invoke(this, m2, (Object[])null);
    }

    public final int hashCode() throws  {
        //刪除了一些tryCatch
        return (Integer)super.h.invoke(this, m0, (Object[])null);
    }

    static {
        //刪除了一些tryCatch
        m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
        m3 = Class.forName("com.proxy.demo.IProduct").getMethod("price");
        m2 = Class.forName("java.lang.Object").getMethod("toString");
        m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    }
}

咱們最主要看的是
構(gòu)造方法

//在這里把val1也就是InvocationHandler傳給了父類Proxy导盅,回憶一下 
//在Proxy.newProxyInstance() 源碼中我讓大家注意的地方 return cons.newInstance(new Object[]{h});這個h其實就是
//newProxyInstance(,揍瑟,h)的第三個參數(shù)白翻,也就是InvocationHandler回調(diào)。
 public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

和這個代理類所有方法中都會調(diào)用的super.h.invoke(this, m3, (Object[])null);

//這個方法的super.h 其實就是剛剛咱們從$Proxy0(InvocationHandler var1)
//傳進去的h绢片,h在這里被invoke,這里是invoke的實現(xiàn)
super.h.invoke(this, m3, (Object[])null);
//也就是它
 new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("我是小fafa");
                Object invoke = method.invoke(facialMask, args);
                System.out.println("面膜現(xiàn)在300塊滤馍,100不存在的");
                return invoke;
            }
        });

總結(jié)一下,代理類執(zhí)行的任何一個方法都會回調(diào)你的InvocationHandler實現(xiàn)類底循。而且通過ProxyGenerator.generateProxyClass()確實動態(tài)生成了字節(jié)碼文件巢株!
那么看完這些咱們也明白了InvocationHandler中invoke(Object proxy, Method method, Object[] args) 這三個參數(shù)分別是什么,

  • 參數(shù)一:Object 生成的代理類對象
  • 參數(shù)二:Method 代理類調(diào)用的方法
  • 參數(shù)三:args 調(diào)用方法時的參數(shù)

好了熙涤,上面咱們了解了所有關(guān)于生成代理類的一些必要方法阁苞,一定要記好這三個參數(shù),因為要為下面講解Retrofit的create()方法做鋪墊。

疑問

我們會有一個疑問灭袁,動態(tài)代理有什么用猬错,因為最后執(zhí)行的還是 面膜的price方法,所以感覺很雞肋,但是他把面膜的方法毫無侵略性的增加了其它方法,在這里用的最多是AOP 面向切面編程領(lǐng)域,攔截個日志什么的茸歧。

Retrofit中的動態(tài)代理

終于寫到了大Boss o(╥﹏╥)os

先看一段代碼,先了解Retrofit用動態(tài)代理解決了什么問題?下面代碼是OkHttp一個GET請求實例,為什么扯上了OkHttp,因為Retrofit就是為了封裝OkHttp

//第一步:初始化
OkHttpClient client = new OkHttpClient();
//第二步獲取請求對象
  Request request = new Request.Builder()
      .url(url)
      .build();
//執(zhí)行請求獲取服務(wù)器響應(yīng)對象
  Response response = client.newCall(request).execute();
  response.body().string();
}

再瞅一下Retrofit如何發(fā)起請求的

//第一步,初始化,配置需要的物料,例如請求地址的根路徑,轉(zhuǎn)化工廠,攔截器什么的
 Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(API_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();

// Create an instance of our GitHub API interface.
//第二步使用動態(tài)代理創(chuàng)建代理類對象
GitHub github = retrofit.create(GitHub.class);

// Create a call instance for looking up Retrofit contributors.
// 然后調(diào)用代理類的方法獲取請求對象
Call<List<Contributor>> call = github.contributors("square", "retrofit");

// Fetch and print a list of the contributors to the library.
//第三步 執(zhí)行請求獲取服務(wù)器響應(yīng)對象
List<Contributor> contributors = call.execute().body();
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}

都是3步走,初始化,創(chuàng)建請求對象,發(fā)送請求獲取數(shù)據(jù).

Retrofit用這兩行替代了OkHttp的這兩行

GitHub github = retrofit.create(GitHub.class);
Call<List<Contributor>> call = github.contributors("square", "retrofit");

public interface GitHub {
    @GET("/repos/{owner}/{repo}/contributors")
    Call<List<Contributor>> contributors(@Path("owner") String owner,@Path("repo") String repo);
}

okttp

//String url ="baseUrl"+/repos/"+"square"+"/"+"retrofit"+"/contributors"
 Request request = new Request.Builder()
      .url(url)
      .build();
  Response response = client.newCall(request).execute();
  String result=response.body().string()
  return GsonUtlis.from(result);

其實優(yōu)勢已經(jīng)出來了,okhttp url的拼接,是不是由我們來做,body的數(shù)據(jù)是字符串或者其它,是不是還要手動處理成gson或者其它,這還只是get請求,Post請求呢?創(chuàng)建請求對象的時候還要創(chuàng)建請求體,但是Retrofit不管你這些,按照暴露的接口,傳參就好了,幫你返回你想要對象,你什么都不用做,只用處理傳入?yún)?shù),獲取結(jié)果.實現(xiàn)了一個黑盒!用戶只需關(guān)心服務(wù)器需要傳遞什么參數(shù),然后拿到結(jié)果.


進入正題

剛剛演示的是Retrofit干了些什么,現(xiàn)在看如何干的

  • 如何動態(tài)產(chǎn)生請求對象Request,弄明白這兩行我們就算大功告成了.
GitHub github = retrofit.create(GitHub.class);
Call<List<Contributor>> call = github.contributors("square", "retrofit");

如果你站在作者的角度出發(fā),你會如何處理,通過一定的規(guī)則產(chǎn)生不同的請求對象呢?而且不能出現(xiàn)很多用戶寫的代碼,因為用戶就是為了省力,才使用這個封裝.

看看作者怎么做的!

  1. 首先定義了一個接口,讓用戶告訴我,你是什么請求,傳了那些參數(shù),想獲得什么對象.
    public interface GitHub {
        @GET("/repos/{owner}/{repo}/contributors")
        Call<List<Contributor>> contributors(
                @Path("owner") String owner,
                @Path("repo") String repo);
    }
  1. 拿到這個接口,處理這個數(shù)據(jù),如何通過接口獲取數(shù)據(jù)呢?還記得動態(tài)代理這幾句代碼么?
new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("我是小fafa");
                Object invoke = method.invoke(facialMask, args);
                System.out.println("面膜現(xiàn)在300塊倦炒,100不存在的");
                return invoke;
            }
        });
  • 參數(shù)一:Object 生成的代理類對象
  • 參數(shù)二:Method 代理類調(diào)用的方法,Method方法可以獲取方法上的注解,主要用的這個
  • 參數(shù)三:args 調(diào)用方法時的參數(shù),
  • 包括方法的返回值

在這里是不是恍然大悟,我想要的東西都有啦.啊哈哈哈哈~

接下來看具體實現(xiàn):

// 源碼2.4.1基礎(chǔ)上
public <T> T create(final Class<T> service) {
    // 刪除一些校驗接口合法性的代碼
    //第一個參數(shù)類加載器,第二個要代理的接口對象,第三個代理對象執(zhí)行方法時的回調(diào)
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();//獲取用戶哪個平臺,有android和java8

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
              //刪除一些其他跟主邏輯無關(guān)的代碼,主要刪了兩行,一個是執(zhí)行繼承Object的方法:tostring,requals,一個是public 非接口的代碼
            return loadServiceMethod(method).invoke(args);
          }
        });
  }

這里最核心的就是loadServiceMethod(method)獲取被代理對象,也就是獲取咱們上面說的面膜,這里你可要注意,這里loadServiceMethod返回的是ServiceMethod,并不是咱們面膜的那個Method了!!!,這里其實就是想借助你調(diào)用的時機獲得一個飽滿的請求對象

ServiceMethod<?> loadServiceMethod(Method method) {
    //從緩存中獲取,因為重新獲取一遍還是挺麻煩的
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);//這里又獲取了一次
      if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method);//生成ServiceMethod的地方
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

上面主要看的是 ServiceMethod.parseAnnotations(this, method);

abstract class ServiceMethod<T> {
  //靜態(tài)方法,
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    Type returnType = method.getGenericReturnType();//獲取你方法的返回值類型
    //刪除一些校驗合法性代碼,
    return new HttpServiceMethod.Builder<Object, T>(retrofit, method).build();
  }

  abstract T invoke(@Nullable Object[] args);
}

上面主要瞅的是new HttpServiceMethod.Builder<Object, T>(retrofit, method).build();

下面是最終的實現(xiàn)!!!!

  HttpServiceMethod<ResponseT, ReturnT> build() {
      requestFactory = RequestFactory.parseAnnotations(retrofit, method);//獲取方法體上面的注解,獲取請求方式,還有retrofit對象獲取請求拼接的url

      callAdapter = createCallAdapter();//獲取CallAdapter,通過這個adapter可以獲取最終的請求對象Call,這里使用適配器模式
      responseType = callAdapter.responseType();//返回類型
     //刪除一些校驗代碼
      responseConverter = createResponseConverter();//獲取responseConverter,它的作用就是采用你提前設(shè)置的Converter,轉(zhuǎn)換出你想要的結(jié)果,例如string->gson
     //面膜終于快出來了,我的淚也出來了
      return new HttpServiceMethod<>(this);
    }

面膜要出來了,這可不是Method的Invoke,而是剛剛new 出來的 HttpServiceMethod.invoke(args)

  @Override ReturnT invoke(@Nullable Object[] args) {
    return callAdapter.adapt(//動態(tài)產(chǎn)生請求對象,(咱們的面膜)
        new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
  }

這里就獲得了咱們的 Call<List<Contributor>> call,這里沒有詳細(xì)展開去分析,獲取requestFactory,callFactory,responseConverter,callAdapter.adapt()的具體實現(xiàn),因為這里和我們動態(tài)代理沒有關(guān)系了,如果想看的話,要看下篇文章了,但是我還沒寫好.O(∩_∩)O哈哈~,最終會分析完Retrofit源碼.

最后

  1. Retrofit 使用動態(tài)產(chǎn)生的代理,產(chǎn)生了動態(tài)的被代理對象----請求對象(面膜),真特么活學(xué)活用呀!
  2. 其實看到這里的時候也解決了我的疑惑,Retrofit到底憑什么那么受歡迎,它最大的特點應(yīng)該就是靈活,通過小的改變,適配你想要的功能.
  3. 能力有限,肯定會有一些錯誤還請批評指正!
  4. 最后:給你一個么么噠(づ ̄ 3 ̄)づ


    此處應(yīng)該有簽名

參考

輕松學(xué),Java 中的代理模式及動態(tài)代理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末软瞎,一起剝皮案震驚了整個濱河市逢唤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌涤浇,老刑警劉巖鳖藕,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異只锭,居然都是意外死亡著恩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喉誊,“玉大人邀摆,你說我怎么就攤上這事∥榍眩” “怎么了栋盹?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長敷矫。 經(jīng)常有香客問我例获,道長,這世上最難降的妖魔是什么曹仗? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任榨汤,我火速辦了婚禮,結(jié)果婚禮上整葡,老公的妹妹穿的比我還像新娘件余。我一直安慰自己,他們只是感情好遭居,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著旬渠,像睡著了一般俱萍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上告丢,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天枪蘑,我揣著相機與錄音,去河邊找鬼岖免。 笑死岳颇,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的颅湘。 我是一名探鬼主播话侧,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼闯参!你這毒婦竟也來了瞻鹏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鹿寨,失蹤者是張志新(化名)和其女友劉穎新博,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脚草,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡赫悄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片埂淮。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡嚼贡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出同诫,到底是詐尸還是另有隱情粤策,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布误窖,位于F島的核電站叮盘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏霹俺。R本人自食惡果不足惜柔吼,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丙唧。 院中可真熱鬧愈魏,春花似錦、人聲如沸想际。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胡本。三九已至牌柄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間侧甫,已是汗流浹背珊佣。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留披粟,地道東北人咒锻。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像守屉,于是被迫代替她去往敵國和親惑艇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359

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