類加載器與雙親委派機(jī)制

前言

什么是類加載器?
類加載器有哪些闰非?
雙親委派機(jī)制是怎么樣的?
什么時(shí)候需要打破雙親委派峭范?
如何打破财松?

以上內(nèi)容網(wǎng)上有大量文章介紹一些基礎(chǔ)概念,這篇《類加載器纱控、雙親委派機(jī)制與打破(Driver辆毡、TCCL、Tomcat等)》甜害、《我竟然被“雙親委派”給虐了》有詳細(xì)介紹舶掖,本文著重從源碼來(lái)講解下我在理解過(guò)程中的疑問(wèn)

用途

先在前面說(shuō),了解這個(gè)有啥用唾那,我認(rèn)為有兩點(diǎn):

  • 可以通過(guò)網(wǎng)絡(luò)访锻、數(shù)據(jù)庫(kù)、接口等多種形式進(jìn)行遠(yuǎn)程class的加載闹获,這個(gè)就很有用了期犬,你的代碼部署在別人那邊,核心class在你這兒
  • 代碼加密避诽,你的代碼部署給用戶龟虎,自定義加載器來(lái)加載解密等操作
  • 熱部署,運(yùn)行過(guò)程中沙庐,直接上傳class文件鲤妥,然后自定義加載佳吞,不用重啟,spring boot那個(gè)熱部署插件就使到這個(gè)
    以上建議還要配合密鑰棉安,時(shí)間等綜合來(lái)考慮底扳,不然單一的還是沒(méi)法保障安全性

疑問(wèn)

1.jvm默認(rèn)的類加載器:AppClassLoader、ExtClassLoader贡耽、Bootstrap ClassLoader衷模;三者是如何在代碼里面沒(méi)有繼承關(guān)系,是如何進(jìn)行逐步委托加載的蒲赂?

首先他們?nèi)皇怯胑xtends進(jìn)行繼承操作的阱冶,是基于組合進(jìn)行的松耦合繼承,可以看ClassLoader類里面的parent屬性滥嘴,其次rt.jar中sun.misc.Launcher類中有兩個(gè)靜態(tài)類AppClassLoader木蹬,ExtClassLoader,查看Launcher的構(gòu)造函數(shù)如下:

public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
            //獲取ext類加載器若皱,點(diǎn)進(jìn)getExtClassLoader()方法可以看到加載的范圍為
            // String var0 = System.getProperty("java.ext.dirs");
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
            //獲取app類加載器镊叁,點(diǎn)進(jìn)getAppClassLoader()方法可以看到final String var1 = System.getProperty("java.class.path");
            //注意此處傳入了var1,創(chuàng)建app時(shí)是尖,將ext傳入意系,跟進(jìn)代碼可以到頂層抽象類ClassLoader.java,代碼見(jiàn)下文
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }
        //注意這個(gè)線程上下文類加載器饺汹,此加載器可以用戶JDBC蛔添、spring等打破雙親加載機(jī)制,默認(rèn)的線程上下文加載器 == app加載器
        Thread.currentThread().setContextClassLoader(this.loader);
        String var2 = System.getProperty("java.security.manager");
        if (var2 != null) {
            SecurityManager var3 = null;
            if (!"".equals(var2) && !"default".equals(var2)) {
                try {
                    var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
                } catch (IllegalAccessException var5) {
                } catch (InstantiationException var6) {
                } catch (ClassNotFoundException var7) {
                } catch (ClassCastException var8) {
                }
            } else {
                var3 = new SecurityManager();
            }

            if (var3 == null) {
                throw new InternalError("Could not create SecurityManager: " + var2);
            }

            System.setSecurityManager(var3);
        }

    }
 private ClassLoader(Void unused, ClassLoader parent) {
        //此時(shí)this指向app兜辞,parent指向ext迎瞧,且parent為該類的成員變量,這種關(guān)系沒(méi)有使用繼承逸吵,采用的基于松耦合的組合關(guān)系
       // 由于啟動(dòng)類加載器是c++實(shí)現(xiàn)的凶硅,在java的視角里是沒(méi)有此對(duì)象的,所以為null扫皱;換而言之足绅,如果this指向是ext,那么他的parent為null韩脑,此處在loadClass方法中有體現(xiàn)氢妈,見(jiàn)下文代碼
        this.parent = parent;
        if (ParallelLoaders.isRegistered(this.getClass())) {
            parallelLockMap = new ConcurrentHashMap<>();
            package2certs = new ConcurrentHashMap<>();
            domains =
                Collections.synchronizedSet(new HashSet<ProtectionDomain>());
            assertionLock = new Object();
        } else {
            // no finer-grained lock; lock on the classloader instance
            parallelLockMap = null;
            package2certs = new Hashtable<>();
            domains = new HashSet<>();
            assertionLock = this;
        }
    }
  protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
           //看看這個(gè)findLoadedClass方法的位置,說(shuō)明相同的類只會(huì)加載一次
           //這就是為很么修改了Class后段多,必須重新啟動(dòng)JVM首量,程序所做的修改才會(huì)生效的原因
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    //父加載器不為空,就一直往上遞歸,直至交給頂層父類加載器加載加缘,結(jié)合上面代碼注釋可知鸭叙,從自定義加載器開(kāi)始,到app拣宏、到ext沈贝,都會(huì)遞歸往上找parent,直到parent == null
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        //這個(gè)代碼是給頂層的啟動(dòng)類加載器調(diào)用的
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                    //進(jìn)行遞歸退出的處理
                }
                //從頂層開(kāi)始蚀浆,每一層類加載器加載不到缀程,就會(huì)逐步往下找可以加載的類加載器,所以自定義加載器是需要重寫(xiě)這個(gè)findClass方法的
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // 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;
        }
    }

2.雙親委派模型中的"雙親"如何理解市俊?

并不是指它有兩個(gè)父類加載器的意思,一個(gè)類加載器只應(yīng)該有一個(gè)父加載器;

  • 父類加載器(parent classloader):它可以替子加載器嘗試加載類滤奈;
    他是整個(gè)JVM加載器的Java代碼可以訪問(wèn)到的類加載器的最頂端摆昧,即是超級(jí)父加載器,拓展類加載器是沒(méi)有父類加載器的蜒程,他的parent為null绅你,由上文loadclass代碼可知,為空就會(huì)調(diào)用findBootstrapClassOrNull()昭躺。
  • 引導(dǎo)類加載器(bootstrap classloader): 子類加載器只能判斷某個(gè)類是否被引導(dǎo)類加載器加載過(guò)忌锯,而不能委托它加載某個(gè)類;換句話說(shuō)领炫,就是子類加載器不能接觸到引導(dǎo)類加載器偶垮,引導(dǎo)類加載器對(duì)其他類加載器而言是透明的。

3.為什么jdbc驅(qū)動(dòng)加載要打破雙親委派

因?yàn)镈river接口是java.sql包下的帝洪,根據(jù)規(guī)定似舵,這個(gè)應(yīng)該是bootstrap classloader來(lái)加載,但是具體的實(shí)現(xiàn)是各個(gè)廠商來(lái)做的葱峡,bootstrap classloader無(wú)法加載實(shí)現(xiàn)類砚哗,這個(gè)時(shí)候就需要子類加載器加載(加載過(guò)程是逐步往上遞歸,父類加載不到逐步由子類來(lái)加載砰奕,由此時(shí)直接交給app來(lái)加載不就行了么蛛芥,返回為null,直接走app classloader 的findclass方法進(jìn)行加載不就可以么)

針對(duì)上面?zhèn)€問(wèn)題军援,重新捋下邏輯
1.jdbc的加載是由DriverManager來(lái)執(zhí)行仅淑,這個(gè)類在rt下面,由根加載器來(lái)加載
2.該類有個(gè)靜態(tài)代碼塊盖溺,里面有l(wèi)oadInitialDrivers()方法漓糙,然后跟進(jìn)方法里面可以看到 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
//這兒涉及spi機(jī)制來(lái)加載廠商的實(shí)現(xiàn)
3.廠商的代碼肯定不在rt包里面,目前的加載器是根加載器烘嘱,無(wú)法加載廠商實(shí)現(xiàn)昆禽,jdk的方案是代碼如下:

public static <S> ServiceLoader<S> load(Class<S> service) {
      ClassLoader cl = Thread.currentThread().getContextClassLoader();
       return ServiceLoader.load(service, cl);
   }

可以看到是在當(dāng)前根加載器執(zhí)行加載期間蝗蛙,從當(dāng)前線程獲取到上線文加載器,去加載廠商的實(shí)現(xiàn)類

我的問(wèn)題來(lái)了:這兒如果不用上下文來(lái)加載醉鳖,直接返回加載不到捡硅,return null; 然后直接走app classloader 的findclass方法進(jìn)行加載不就可以么?
答:被這個(gè)問(wèn)題困擾了幾天,想不通盗棵,后面猜測(cè)如果返回為空壮韭,那之前根加載到的DriverManager怎么辦,是直接放棄了么纹因,我理解的是整個(gè)DriverManager加載和廠商驅(qū)動(dòng)加載是一體的原子性的喷屋,必須要在根加載器加載到一半的時(shí)候繼續(xù)去加載廠商的,如果這個(gè)時(shí)候返回為空瞭恰,那么后面app是可以加載廠商實(shí)現(xiàn)屯曹,但是DriverManager怎么辦,不加載了么惊畏,要加載的話恶耽,又是根加載器來(lái)加載

這個(gè)理解不知道對(duì)不對(duì)

4.為什么tomcat要打破雙親委派模型的?

從問(wèn)題1我們知道颜启,類的加載是逐步委托給頂層的類加載器進(jìn)行加載偷俭,加載不到時(shí)才會(huì)由下面的加載器進(jìn)行加載;
假設(shè)我們tomcat容器中有三個(gè)server缰盏,server1的jackson是1.0涌萤;server2的jackson是2.0;server3的jackson是3.0乳规;
三個(gè)版本里面的某個(gè)類的方法里面的實(shí)現(xiàn)可能不一樣形葬,但是由于雙親委派機(jī)制的存在,都會(huì)交由頂層類加載器來(lái)加載暮的,如果沒(méi)有自定義笙以,那么應(yīng)該是app加載,就會(huì)導(dǎo)致該類最終會(huì)被覆蓋成某個(gè)版本冻辩;(類在jvm中的唯一性由類加載器名和類全路徑?jīng)Q定)
因?yàn)槲覀兊娜齻€(gè)server是不同的應(yīng)用猖腕,所以我們需要各個(gè)server進(jìn)行隔離,獨(dú)自加載恨闪,不應(yīng)該使用雙親委派交由頂層加載

  1. tomcat是如何打破雙親委派模型的?
    首先推薦一篇優(yōu)秀的文章 [Tomcat 的架構(gòu)有哪些牛逼之處倘感?](https://mp.weixin.qq.com/s/_bsAOTA10fGDJsz2jCYivg) 從架構(gòu)、實(shí)現(xiàn)等多個(gè)維度層面進(jìn)行分析咙咽,文章較難老玛,需要多次閱讀

org.apache.catalina.loader.WebappClassLoader,該類重寫(xiě)了findclass方法

ublic Class<?> findClass(String name) throws ClassNotFoundException {
    ...

    Class<?> clazz = null;
    try {
            //1. 先在 Web 應(yīng)用目錄下查找類
            clazz = findClassInternal(name);
    }  catch (RuntimeException e) {
           throw e;
       }

    if (clazz == null) {
    try {
            //2. 如果在本地目錄沒(méi)有找到,交給父加載器去查找
            clazz = super.findClass(name);
    }  catch (RuntimeException e) {
           throw e;
       }

    //3. 如果父類也沒(méi)找到蜡豹,拋出 ClassNotFoundException
    if (clazz == null) {
        throw new ClassNotFoundException(name);
     }

    return clazz;
}

同樣的也重寫(xiě)了loadclass方法

public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {

    synchronized (getClassLoadingLock(name)) {

        Class<?> clazz = null;

        //1. 先在本地 cache 查找該類是否已經(jīng)加載過(guò)(主要是tomcat自定義加載器中查找)
        clazz = findLoadedClass0(name);
        if (clazz != null) {
            if (resolve)
                resolveClass(clazz);
            return clazz;
        }

        //2. 從系統(tǒng)類加載器的 cache 中查找是否加載過(guò)(從jvm加載的類里面查找)
        clazz = findLoadedClass(name);
        if (clazz != null) {
            if (resolve)
                resolveClass(clazz);
            return clazz;
        }

        // 3. 嘗試用 ExtClassLoader 類加載器類加載(此處是精髓麸粮,注意是ext,不是app镜廉,基礎(chǔ)類和擴(kuò)展類都是交由雙親來(lái)加載弄诲,避免了覆蓋 JRE 核心類,保證虛擬機(jī)的正常運(yùn)行)
        ClassLoader javaseLoader = getJavaseClassLoader();
        try {
            clazz = javaseLoader.loadClass(name);
            if (clazz != null) {
                if (resolve)
                    resolveClass(clazz);
                return clazz;
            }
        } catch (ClassNotFoundException e) {
            // Ignore
        }

        // 4. 嘗試在本地目錄搜索 class 并加載(這兒就是tomcat自定義加載器加載了)
        try {
            clazz = findClass(name);
            if (clazz != null) {
                if (resolve)
                    resolveClass(clazz);
                return clazz;
            }
        } catch (ClassNotFoundException e) {
            // Ignore
        }

        // 5. 嘗試用系統(tǒng)類加載器 (也就是 AppClassLoader) 來(lái)加載
       (其他類還有交由app加載)
            try {
                clazz = Class.forName(name, false, parent);
                if (clazz != null) {
                    if (resolve)
                        resolveClass(clazz);
                    return clazz;
                }
            } catch (ClassNotFoundException e) {
                // Ignore
            }
       }

    //6. 上述過(guò)程都加載失敗娇唯,拋出異常
    throw new ClassNotFoundException(name);
}
tomcat加載類關(guān)系圖.png
  • WebAppClassLoader
    Tomcat 的解決方案是自定義一個(gè)類加載器 WebAppClassLoader齐遵, 并且給每個(gè) Web 應(yīng)用創(chuàng)建一個(gè)類加載器實(shí)例。我們知道塔插,Context 容器組件對(duì)應(yīng)一個(gè) Web 應(yīng)用梗摇,因此,每個(gè) Context容器負(fù)責(zé)創(chuàng)建和維護(hù)一個(gè) WebAppClassLoader加載器實(shí)例佑淀。這背后的原理是留美,不同的加載器實(shí)例加載的類被認(rèn)為是不同的類,即使它們的類名相同伸刃。這就相當(dāng)于在 Java 虛擬機(jī)內(nèi)部創(chuàng)建了一個(gè)個(gè)相互隔離的 Java 類空間,每一個(gè) Web 應(yīng)用都有自己的類空間逢倍,Web 應(yīng)用之間通過(guò)各自的類加載器互相隔離捧颅。

  • SharedClassLoader
    本質(zhì)需求是兩個(gè) Web 應(yīng)用之間怎么共享庫(kù)類,并且不能重復(fù)加載相同的類。在雙親委托機(jī)制里较雕,各個(gè)子加載器都可以通過(guò)父加載器去加載類碉哑,那么把需要共享的類放到父加載器的加載路徑下不就行了嗎。
    因此 Tomcat 的設(shè)計(jì)者又加了一個(gè)類加載器 SharedClassLoader亮蒋,作為 WebAppClassLoader的父加載器扣典,專門來(lái)加載 Web 應(yīng)用之間共享的類。如果 WebAppClassLoader自己沒(méi)有加載到某個(gè)類慎玖,就會(huì)委托父加載器 SharedClassLoader去加載這個(gè)類贮尖,SharedClassLoader會(huì)在指定目錄下加載共享類,之后返回給 WebAppClassLoader趁怔,這樣共享的問(wèn)題就解決了湿硝。

  • CatalinaClassloader
    如何隔離 Tomcat 本身的類和 Web 應(yīng)用的類?
    要共享可以通過(guò)父子關(guān)系润努,要隔離那就需要兄弟關(guān)系了关斜。兄弟關(guān)系就是指兩個(gè)類加載器是平行的,它們可能擁有同一個(gè)父加載器铺浇,基于此 Tomcat 又設(shè)計(jì)一個(gè)類加載器 CatalinaClassloader痢畜,專門來(lái)加載 Tomcat 自身的類。
    這樣設(shè)計(jì)有個(gè)問(wèn)題,那 Tomcat 和各 Web 應(yīng)用之間需要共享一些類時(shí)該怎么辦呢丁稀?
    老辦法吼拥,還是再增加一個(gè) CommonClassLoader,作為 CatalinaClassloader和 SharedClassLoader的父加載器二驰。CommonClassLoader能加載的類都可以被 CatalinaClassLoader和 SharedClassLoader使用
    //TODO

6.類加載成功后會(huì)立即調(diào)用構(gòu)造函數(shù)進(jìn)行實(shí)例化么

不會(huì)扔罪,如果涉及到new等創(chuàng)建對(duì)象才會(huì)實(shí)例化,類加載后是會(huì)初始化靜態(tài)代碼塊桶雀,靜態(tài)變量等矿酵,類的初始化和對(duì)象的初始化是兩個(gè)事情

7.自定義類加載器是如何保障加載器執(zhí)行順序的,即app怎么成為自定義加載器的parent

ClassLoader類里面默認(rèn)的parent是app

protected ClassLoader() {
       this(checkCreateClassLoader(), getSystemClassLoader());
}

8.當(dāng)實(shí)現(xiàn)自定義類加載器時(shí)不應(yīng)重寫(xiě)loadClass()矗积,除非你不需要雙親委派機(jī)制全肮。要重寫(xiě)的是findClass()的邏輯,也就是尋找并加載類的方式

總結(jié):

  • 參考classloader里面申明 private final ClassLoader parent;然后子類基于組合的集成方式棘捣,之后遞歸雙親委派【從這兒我們可以抄作業(yè)辜腺,就是我們?cè)O(shè)計(jì)底層框架,借鑒這個(gè)模式乍恐,從而實(shí)現(xiàn)父類調(diào)用子類的方
  • tomcat架構(gòu)運(yùn)用模板方法模式评疗。分別運(yùn)用了組合模式、觀察者模式茵烈、骨架抽象類和模板方法百匆,需要自己去體會(huì)如何使用的,使用和不使用的區(qū)別
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末呜投,一起剝皮案震驚了整個(gè)濱河市加匈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌仑荐,老刑警劉巖雕拼,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異粘招,居然都是意外死亡啥寇,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門男图,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)示姿,“玉大人,你說(shuō)我怎么就攤上這事逊笆≌淮粒” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵难裆,是天一觀的道長(zhǎng)子檀。 經(jīng)常有香客問(wèn)我镊掖,道長(zhǎng),這世上最難降的妖魔是什么褂痰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任亩进,我火速辦了婚禮,結(jié)果婚禮上缩歪,老公的妹妹穿的比我還像新娘归薛。我一直安慰自己,他們只是感情好匪蝙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布主籍。 她就那樣靜靜地躺著,像睡著了一般逛球。 火紅的嫁衣襯著肌膚如雪千元。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,146評(píng)論 1 297
  • 那天颤绕,我揣著相機(jī)與錄音幸海,去河邊找鬼。 笑死奥务,一個(gè)胖子當(dāng)著我的面吹牛物独,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播氯葬,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼议纯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了溢谤?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤憨攒,失蹤者是張志新(化名)和其女友劉穎世杀,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體肝集,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瞻坝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了杏瞻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片所刀。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖捞挥,靈堂內(nèi)的尸體忽然破棺而出浮创,到底是詐尸還是另有隱情,我是刑警寧澤砌函,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布斩披,位于F島的核電站溜族,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏垦沉。R本人自食惡果不足惜煌抒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望厕倍。 院中可真熱鬧寡壮,春花似錦、人聲如沸讹弯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)闸婴。三九已至坏挠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間邪乍,已是汗流浹背降狠。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留庇楞,地道東北人榜配。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像吕晌,于是被迫代替她去往敵國(guó)和親蛋褥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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

  • 京心??達(dá)三店:何海港 2021年8月 4日 落地真經(jīng) 嚴(yán)格就是愛(ài)睛驳,放縱既是害 油卡目標(biāo):40張烙心、完成7張 正能量...
    fe20d692e40e閱讀 124評(píng)論 0 0
  • 事業(yè)編制,在我們單位的天花邊就是科長(zhǎng)乏沸,接下去就是退二線淫茵,而我已經(jīng)完成了一半,人生漫漫的旅途蹬跃,我已經(jīng)完成了一半匙瘪,這還...
    跑步改變命運(yùn)閱讀 163評(píng)論 0 0
  • 學(xué)習(xí)《民法典》心得(二十五) ...
    奉法如天閱讀 160評(píng)論 0 4
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭蝶缀,有人歡樂(lè)有人憂愁丹喻,有人驚喜有人失落,有的覺(jué)得收獲滿滿有...
    陌忘宇閱讀 8,535評(píng)論 28 53
  • 人工智能是什么翁都?什么是人工智能碍论?人工智能是未來(lái)發(fā)展的必然趨勢(shì)嗎?以后人工智能技術(shù)真的能達(dá)到電影里機(jī)器人的智能水平嗎...
    ZLLZ閱讀 3,776評(píng)論 0 5