阿里P7架構(gòu)師通過源碼淺析Java中的資源加載

一. 前提

最近在做一個(gè)基礎(chǔ)組件項(xiàng)目剛好需要用到JDK中的資源加載,這里說到的資源包括類文件和其他靜態(tài)資源谐檀,剛好需要重新補(bǔ)充一下類加載器和資源加載的相關(guān)知識(shí)抡谐,整理成一篇文章。

二. 什么是類加載器

虛擬機(jī)設(shè)計(jì)團(tuán)隊(duì)把類加載階段中的”通過一個(gè)類的全限定名來獲取描述此類的二進(jìn)制字節(jié)流”這個(gè)動(dòng)作放到了Java虛擬機(jī)外部實(shí)現(xiàn)稚补,以便讓應(yīng)用程序自己決定如何去獲取所需要的類童叠,而實(shí)現(xiàn)這個(gè)動(dòng)作的代碼模塊稱為”類加載器(ClassLoader)”。

類加載器雖然只用于實(shí)現(xiàn)類加載的功能课幕,但是它在Java程序中起到的作用不局限于類加載階段厦坛。對(duì)于任意一個(gè)類,都需要由加載它的類加載器和這個(gè)類本身一同確立類在Java虛擬機(jī)中的唯一性乍惊,每一個(gè)類加載器杜秸,都擁有一個(gè)獨(dú)立的類命名空間。上面這句話直觀來說就是:比較兩個(gè)類是否”相等”润绎,只有在這兩個(gè)類是由同一個(gè)類加載器加載的前提下才有意義撬碟,否則,即使這個(gè)兩個(gè)類是來源于同一個(gè)Class文件莉撇,被同一個(gè)虛擬機(jī)加載呢蛤,只要加載它們的類加載器不同,那么這兩個(gè)類必然”不相等”棍郎。這里說到的”相等”包括代表類的Class對(duì)象的equals()方法其障、isAssignableFrom()方法、isInstance()方法的返回結(jié)果涂佃,也包括使用instanceOf關(guān)鍵字做對(duì)象所屬關(guān)系判定等情況励翼。

類和加載它的類加載器確定類在Java虛擬機(jī)中的唯一性這個(gè)特點(diǎn)為后來出現(xiàn)的熱更新類、熱部署等技術(shù)提供了基礎(chǔ)辜荠。

三. 雙親委派模型

從Java虛擬機(jī)的角度來看汽抚,只有兩種不同的類加載器

  • 第一種是啟動(dòng)類加載器(Bootstrap ClassLoader),這個(gè)類加載器使用C++編程語言實(shí)現(xiàn)伯病,是虛擬機(jī)的一部分造烁。
  • 另一種是其他的類加載器,這些類加載器都是由Java語言實(shí)現(xiàn)午笛,獨(dú)立于虛擬機(jī)之外膨蛮,一般就是內(nèi)部于JDK中,它們都繼承自抽象類加載器java.lang.ClassLoader季研。

JDK中提供幾個(gè)系統(tǒng)級(jí)別的類加載器

  • 啟動(dòng)類加載器(Bootstrap ClassLoader):這個(gè)類加載器負(fù)責(zé)將存放在${JAVA_HONE}\lib目錄中敞葛,或者被XbootstrapPath參數(shù)所指定的目錄中,并且是虛擬機(jī)基于一定規(guī)則(如文件名稱規(guī)則与涡,如rt.jar)標(biāo)識(shí)的類庫加載到虛擬機(jī)內(nèi)存中惹谐。啟動(dòng)類加載器無法被Java程序直接引用持偏,開發(fā)者在編寫自定義類加載器如果想委派到啟動(dòng)類加載器只需直接使用null替代即可。
  • 擴(kuò)展類加載器(Extension ClassLoader):這個(gè)類加載器由sun.misc.Launcher的靜態(tài)內(nèi)部類ExtClassLoader實(shí)現(xiàn)氨肌,它負(fù)責(zé)加載${JAVA_HONE}\lib\ext目錄中鸿秆,或者通過java.ext.dirs系統(tǒng)變量指定的路徑中的所有類庫,開發(fā)者可以直接使用此類加載器怎囚。
  • 應(yīng)用程序類加載器(Application ClassLoader):這個(gè)類加載器由sun.misc.Launcher的靜態(tài)內(nèi)部類AppClassLoader實(shí)現(xiàn)卿叽,但是由于這個(gè)類加載器的實(shí)例是ClassLoader中靜態(tài)方法getSystemClassLoader()中的返回值,一般也稱它為系統(tǒng)類加載器恳守。它負(fù)責(zé)加載用戶類路徑(ClassPath)上所指定的類庫考婴,開發(fā)者可以直接使用這個(gè)類加載器,如果應(yīng)用程序中沒有自定義過自實(shí)現(xiàn)的類加載器催烘,一般情況下這個(gè)系統(tǒng)類加載器就是應(yīng)用程序中默認(rèn)使用的類加載器沥阱。
  • 線程上下文類加載器(Thread Context ClassLoader):這個(gè)在下一小節(jié)”破壞雙親委派模型”再分析。

Java開發(fā)者開發(fā)出來的Java應(yīng)用程序都是由上面四種類加載器相互配合進(jìn)行類加載的伊群,如果有必要還可以加入自定義的類加載器考杉。其中,啟動(dòng)類加載器舰始、擴(kuò)展類加載器崇棠、應(yīng)用程序類加載器和自定義類加載器之間存在著一定的關(guān)系:

上圖展示的類加載器之間的層次關(guān)系稱為雙親委派模型(Parents Delegation Model)。雙親委派模型要求除了頂層的類加載器(Java中頂層的類加載器一般是Bootstrap ClassLoader)丸卷,其他的類加載器都應(yīng)當(dāng)有自己的父類加載器枕稀。這些類加載器之間的父子關(guān)系一般不會(huì)以繼承(Inheritance)的關(guān)系來實(shí)現(xiàn),而是通過組合(Composition)的關(guān)系實(shí)現(xiàn)及老。類加載器層次關(guān)系這一點(diǎn)可以通過下面的代碼驗(yàn)證一下:

public class Main {
    public static void main(String[] args) throws Exception{
        ClassLoader classLoader = Main.class.getClassLoader();
        System.out.println(classLoader);
        System.out.println(classLoader.getParent());
        System.out.println(classLoader.getParent().getParent());
    }
}
//輸出結(jié)果,最后的null說明是Bootstrap ClassLoader
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@4629104a
null

雙親委派模型的工作機(jī)制:如果一個(gè)類加載器收到了類加載的請(qǐng)求范抓,它首先不會(huì)自己嘗試去加載這個(gè)類骄恶,而是把這個(gè)請(qǐng)求委派給父類加載器去完成,每一個(gè)層次的類加載器都是如此匕垫,因此所有的類加載請(qǐng)求最終都應(yīng)該傳送到頂層的類加載器中僧鲁,只有當(dāng)父類加載器反饋?zhàn)约簾o法完成當(dāng)前的類加載請(qǐng)求的時(shí)候(也就是在它的搜索范圍中沒有找到所需要的類),子類加載器才會(huì)嘗試自己去加載類象泵。不過這里有一點(diǎn)需要注意寞秃,每一個(gè)類加載器都會(huì)緩存已經(jīng)加載過的類,也就是重復(fù)加載一個(gè)已經(jīng)存在的類偶惠,那么就會(huì)從已經(jīng)加載的緩存中加載春寿,如果從當(dāng)前類加載的緩存中判斷類已經(jīng)加載過,那么直接返回忽孽,否則會(huì)委派類加載請(qǐng)求到父類加載器绑改。這個(gè)緩存機(jī)制在AppClassLoaderExtensionClassLoader中都存在谢床,至于BootstrapClassLoader未知。

雙親委派模型的優(yōu)勢(shì):使用雙親委派模型來組織類加載器之間的關(guān)系厘线,一個(gè)比較顯著的優(yōu)點(diǎn)是Java類隨著加載它的類加載器一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系识腿。例如java.lang包中的類庫,它存放在rt.jar中造壮,無論使用哪一個(gè)類加載加載java.lang包中的類渡讼,最終都是委派給處于模型頂層的啟動(dòng)類加載器進(jìn)行加載,因此java.lang包中的類如java.lang.Object類在應(yīng)用程序中的各類加載器環(huán)境中加載的都是同一個(gè)類耳璧。試想成箫,如果可以使用用戶自定義的ClassLoader去加載java.lang.Object,那么用戶應(yīng)用程序中就會(huì)出現(xiàn)多個(gè)java.lang.Object類楞抡,Java類型體系中最基礎(chǔ)的類型也有多個(gè)伟众,類型體系的基礎(chǔ)行為無法保證,應(yīng)用程序也會(huì)趨于混亂召廷。如果嘗試編寫rt.jar中已經(jīng)存在的同類名的類通過自定義的類加載進(jìn)行加載凳厢,將會(huì)接收到虛擬機(jī)拋出的異常。

雙親委派模型的實(shí)現(xiàn):類加載器雙親委派模型的實(shí)現(xiàn)提現(xiàn)在ClassLoader的源碼中竞慢,主要是ClassLoader#loadClass()中先紫。

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}
protected Class<?> loadClass(String name, Boolean resolve) throws ClassNotFoundException{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                //父加載器不為null,說明父加載器不是BootstrapClassLoader
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    //父加載器為null筹煮,說明父加載器是BootstrapClassLoader
                    c = findBootstrapClassOrNull(name);
                }
            }
            catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            //所有的父加載加載失敗,則使用當(dāng)前的類加載器進(jìn)行類加載
            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);
                //記錄一些統(tǒng)計(jì)數(shù)據(jù)如加載耗時(shí)遮精、計(jì)數(shù)等
                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

四. 破壞雙親委派模型

雙親委派模型在Java發(fā)展歷史上出現(xiàn)了三次比較大”被破壞”的情況:

  • ClassLoader在JDK1.0已經(jīng)存在,JDK1.2為了引入雙親委派模型并且需要向前兼容败潦,java.lang.ClassLoader類添加了一個(gè)新的protected的findClass()方法本冲,在這之前,用戶去繼承java.lang.ClassLoader只能重寫其loadClass()方法才能實(shí)現(xiàn)自己的目標(biāo)劫扒。
  • 雙親委派模型自身存在缺陷:雙親委派很好地解決了各個(gè)類加載器的基礎(chǔ)類的加載的統(tǒng)一問題(越基礎(chǔ)的類由越上層的類加載器加載)檬洞,這些所謂的基礎(chǔ)類就是大多數(shù)情況下作為用戶調(diào)用的基礎(chǔ)類庫和基礎(chǔ)API,但是無法解決這些基礎(chǔ)類需要回調(diào)用戶的代碼這一個(gè)問題沟饥,典型的例子就是JNDI添怔。JNDI的類庫代碼是啟動(dòng)類加載器加載的,但是它需要調(diào)用獨(dú)立廠商實(shí)現(xiàn)并且部署在應(yīng)用的ClassPath的JNDI的服務(wù)接口提供者(SPI贤旷,即是Service Provider Interface)的代碼广料,但是啟動(dòng)類加載器無法加載ClassPath下的類庫。為了解決這個(gè)問題幼驶,Java設(shè)計(jì)團(tuán)隊(duì)引入了不優(yōu)雅的設(shè)計(jì):線程上下文類加載器(Thread Context ClassLoader)艾杏,這個(gè)類加載器可以通過java.lang.Thread類的setContextClassLoader()設(shè)置,這樣子盅藻,JNDI服務(wù)就可以使用線程上下文類加載器去加載所需的SPI類庫糜颠,但是父類加載器中請(qǐng)求子類加載器去加載類這一點(diǎn)已經(jīng)打破了雙親委派模型汹族。目前,JNDI其兴、JDBC顶瞒、JCE、JAXB和JBI等模塊都是通過此方式實(shí)現(xiàn)元旬。
  • 基于用戶對(duì)應(yīng)用程序動(dòng)態(tài)性的熱切追求:如代碼熱替換(HotSwap)榴徐、熱模塊部署等,說白了就是希望應(yīng)用程序能像我們的計(jì)算機(jī)外設(shè)那樣可以熱插拔匀归,因此催生出JSR-291以及它的業(yè)界實(shí)現(xiàn)OSGi坑资,而OSGi定制了自己的類加載規(guī)則,不再遵循雙親委派模型穆端,因此它可以通過自定義的類加載器機(jī)制輕易實(shí)現(xiàn)模塊的熱部署袱贮。

四. JDK中提供的資源加載API

前邊花大量的篇幅去分析類加載器的預(yù)熱知識(shí),是因?yàn)镴DK中的資源加載依賴于類加載器(其實(shí)類文件本來就是資源文件的一種体啰,類加載的過程也是資源加載的過程)攒巍。這里先列舉出JDK中目前常用的資源(Resource)加載的API,先看ClassLoader中提供的方法荒勇。

1. ClassLoader提供的資源加載API

//1.實(shí)例方法
public URL getResource(String name)
//這個(gè)方法僅僅是調(diào)用getResource(String name)返回URL實(shí)例直接調(diào)用URL實(shí)例的openStream()方法
public InputStream getResourceAsStream(String name)
//這個(gè)方法是getResource(String name)方法的復(fù)數(shù)版本
public Enumeration<URL> getResources(String name) throws IOException
//2.靜態(tài)方法
public static URL getSystemResource(String name)
//這個(gè)方法僅僅是調(diào)用getSystemResource(String name)返回URL實(shí)例直接調(diào)用URL實(shí)例的openStream()方法
public static InputStream getSystemResourceAsStream(String name)
//這個(gè)方法是getSystemResources(String name)方法的復(fù)數(shù)版本
public static Enumeration<URL> getSystemResources(String name)

總的來看柒莉,只有兩個(gè)方法需要分析:getResource(String name)和getSystemResource(String name)。查看getResource(String name)的源碼:

public URL getResource(String name) {
    URL url;
    if (parent != null) {
        url = parent.getResource(name);
    } else {
        url = getBootstrapResource(name);
    }
    if (url == null) {
        url = findResource(name);
    }
    return url;
}

是否似曾相識(shí)?這里明顯就是使用了類加載過程中類似的雙親委派模型進(jìn)行資源加載沽翔,這個(gè)方法在API注釋中描述通常用于加載數(shù)據(jù)資源如images兢孝、audio、text等等仅偎,資源名稱需要使用路徑分隔符’/‘跨蟹。getResource(String name)方法中查找的根路徑我們可以通過下面方法驗(yàn)證:

public class ResourceLoader {
    public static void main(String[] args) throws Exception {
        ClassLoader classLoader = ResourceLoader.class.getClassLoader();
        URL resource = classLoader.getResource("");
        System.out.println(resource);
    }
}
//輸出:file:/D:/Projects/rxjava-seed/target/classes/

很明顯輸出的結(jié)果就是當(dāng)前應(yīng)用的ClassPath,總結(jié)來說:ClassLoader#getResource(String name)是基于用戶應(yīng)用程序的ClassPath搜索資源橘沥,資源名稱必須使用路徑分隔符’/‘去分隔目錄窗轩,但是不能以’/‘作為資源名的起始,也就是不能這樣使用:classLoader.getResource("/img/doge.jpg")威恼。接著我們?cè)倏匆幌翪lassLoader#getSystemResource(String name)的源碼:

public static URL getSystemResource(String name) {
    //實(shí)際上Application ClassLoader一般不會(huì)為null
    ClassLoader system = getSystemClassLoader();
    if (system == null) {
        return getBootstrapResource(name);
    }
    return system.getResource(name);
}

此方法優(yōu)先使用應(yīng)用程序類加載器進(jìn)行資源加載品姓,如果應(yīng)用程序類加載器為null(其實(shí)這種情況很少見)寝并,則使用啟動(dòng)類加載器進(jìn)行資源加載箫措。如果應(yīng)用程序類加載器不為null的情況下,它實(shí)際上退化為ClassLoader#getResource(String name)方法衬潦。

總結(jié)一下:ClassLoader提供的資源加載的方法中的核心方法是ClassLoader#getResource(String name)斤蔓,它是基于用戶應(yīng)用程序的ClassPath搜索資源,遵循”資源加載的雙親委派模型”镀岛,資源名稱必須使用路徑分隔符’/‘去分隔目錄弦牡,但是不能以’/‘作為資源名的起始字符友驮,其他幾個(gè)方法都是基于此方法進(jìn)行衍生,添加復(fù)數(shù)操作等其他操作驾锰。getResource(String name)方法不會(huì)顯示拋出異常卸留,當(dāng)資源搜索失敗的時(shí)候,會(huì)返回null椭豫。

2. Class提供的資源加載API

java.lang.Class中也提供了資源加載的方法耻瑟,如下:

public java.net.URL getResource(String name) {
    name = resolveName(name);
    ClassLoader cl = getClassLoader0();
    if (cl==null) {
        // A system class.
        return ClassLoader.getSystemResource(name);
    }
    return cl.getResource(name);
}
public InputStream getResourceAsStream(String name) {
    name = resolveName(name);
    ClassLoader cl = getClassLoader0();
    if (cl==null) {
        // A system class.
        return ClassLoader.getSystemResourceAsStream(name);
    }
    return cl.getResourceAsStream(name);
}

從上面的源碼來看,Class#getResource(String name)和Class#getResourceAsStream(String name)分別比ClassLoader#getResource(String name)和ClassLoader#getResourceAsStream(String name)只多了一步赏酥,就是搜索之前先進(jìn)行資源名稱的預(yù)處理resolveName(name)喳整,我們重點(diǎn)看這個(gè)方法做了什么:

private String resolveName(String name) {
    if (name == null) {
        return name;
    }
    if (!name.startsWith("/")) {
        Class<?> c = this;
        while (c.isArray()) {
            c = c.getComponentType();
        }
        String baseName = c.getName();
        int index = baseName.lastIndexOf('.');
        if (index != -1) {
            name = baseName.substring(0, index).replace('.', '/')
                                +"/"+name;
        }
    } else {
        name = name.substring(1);
    }
    return name;
}

邏輯相對(duì)比較簡(jiǎn)單

  • 如果資源名稱以’/‘開頭,那么直接去掉’/‘裸扶,這個(gè)時(shí)候的資源查找實(shí)際上退化為ClassPath中的資源查找框都。
  • 如果資源名稱不以’/‘開頭,那么解析出當(dāng)前類的實(shí)際類型(因?yàn)楫?dāng)前類有可能是數(shù)組)呵晨,取出類型的包路徑魏保,替換包路徑中的’.’為’/‘,再拼接原來的資源名稱何荚。舉個(gè)例子:”club.throwable.Main.class”中調(diào)用了Main.class.getResource("doge.jpg")囱淋,那么這個(gè)調(diào)用的處理資源名稱的結(jié)果就是club/throwable/doge.jpg。

小結(jié):如果看過我之前寫過的一篇URL和URI相關(guān)的文章就清楚餐塘,實(shí)際上Class#getResource(String name)和Class#getResourceAsStream(String name)的資源名稱處理類似于相對(duì)URL的處理妥衣,而”相對(duì)URL的處理”的根路徑就是應(yīng)用程序的ClassPath。如果資源名稱以’/‘開頭戒傻,那么相當(dāng)于從ClassPath中加載資源税手,如果資源名稱不以’/‘開頭,那么相當(dāng)于基于當(dāng)前類的實(shí)際類型的包目錄下加載資源需纳。

實(shí)際上類似這樣的資源加載方式在File類中也存在芦倒,這里就不再展開。

五. 小結(jié)

理解JDK中的資源加載方式有助于編寫一些通用的基礎(chǔ)組件不翩,像Spring里面的ResourceLoader兵扬、ClassPathResource這里比較實(shí)用的工具也是基于JDK資源加載的方式編寫出來。

說實(shí)話口蝠,類加載器的”雙親委派模型”和”破壞雙親委派模型”是常見的面試題相關(guān)內(nèi)容器钟,這里可以簡(jiǎn)單列舉兩個(gè)面試題:

  • 談?wù)剬?duì)類加載器的”雙親委派模型”的理解。
  • 為什么要引入線程上下文類加載器(或者是對(duì)于問題1有打破這個(gè)模型的案例嗎)?

希望這篇文章能幫助你理解和解決這兩個(gè)問題妙蔗。

寫在最后

  • 第一:看完點(diǎn)贊傲霸,感謝您的認(rèn)可;
  • ...
  • 第二:隨手轉(zhuǎn)發(fā),分享知識(shí)昙啄,讓更多人學(xué)習(xí)到穆役;
  • ...
  • 第三:記得點(diǎn)關(guān)注,每天更新的J崃荨9⒈摇!
  • ...
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末韧拒,一起剝皮案震驚了整個(gè)濱河市掰读,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叭莫,老刑警劉巖蹈集,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異雇初,居然都是意外死亡拢肆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門靖诗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來郭怪,“玉大人,你說我怎么就攤上這事刊橘”刹牛” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵促绵,是天一觀的道長(zhǎng)攒庵。 經(jīng)常有香客問我,道長(zhǎng)败晴,這世上最難降的妖魔是什么浓冒? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮尖坤,結(jié)果婚禮上稳懒,老公的妹妹穿的比我還像新娘。我一直安慰自己慢味,他們只是感情好场梆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纯路,像睡著了一般或油。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上感昼,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天装哆,我揣著相機(jī)與錄音罐脊,去河邊找鬼定嗓。 笑死蜕琴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宵溅。 我是一名探鬼主播凌简,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼恃逻!你這毒婦竟也來了雏搂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤寇损,失蹤者是張志新(化名)和其女友劉穎凸郑,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矛市,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芙沥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了浊吏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片而昨。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖找田,靈堂內(nèi)的尸體忽然破棺而出歌憨,到底是詐尸還是另有隱情,我是刑警寧澤墩衙,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布务嫡,位于F島的核電站,受9級(jí)特大地震影響漆改,放射性物質(zhì)發(fā)生泄漏植袍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一籽懦、第九天 我趴在偏房一處隱蔽的房頂上張望于个。 院中可真熱鬧,春花似錦暮顺、人聲如沸厅篓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽羽氮。三九已至,卻和暖如春惫恼,著一層夾襖步出監(jiān)牢的瞬間档押,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留令宿,地道東北人叼耙。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像粒没,于是被迫代替她去往敵國和親筛婉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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