Spring GetBean流程

?第一節(jié)講解Spring啟動的時候說到栅迄,Spring內(nèi)部先解析了所有的配置,加載所有的Bean定義后文搂,再根據(jù)需要對Bean進行實例化和初始化斟叼。除開Spring自己主動新建的對象,第一次根據(jù)Bean定義加載對象的動作出現(xiàn)在AbstractApplicationContext的invokeBeanFactoryPostProcessors方法丹莲,該方法會在Spring容器中找出實現(xiàn)了BeanFactoryPostProcessor接口的bean列表并執(zhí)行光坝。根據(jù)之前介紹的內(nèi)容,內(nèi)部主要調(diào)用了AbstractBeanFactory的getBean方法甥材,這節(jié)將對該方法進行講解盯另。

一、getBean

?在這之前洲赵,先介紹BeanFactory的層次結(jié)構(gòu)鸳惯,如下:

file

涉及到的接口和實現(xiàn)類為:

?AliasRegistry:別名管理接口,定義了別名管理的功能

?SimpleAliasRegistry:AliasRegistry的默認實現(xiàn)叠萍,內(nèi)部用一個

?ConcurrentHashMap:管理別名

?SingletonBeanRegistry:單例實例管理接口芝发,定義了單例管理的功能

?DefaultSingletonBeanRegistry:單例管理實現(xiàn)類,內(nèi)部用Map維護著被實例化后的所有單例苛谷、單例工廠等相關信息辅鲸。Map的鍵為bean的唯一標識,Spring內(nèi)部成為raw name腹殿,一般等同于Bean定義中的id或者name或者別名等独悴,具體規(guī)則可以從上節(jié)BeanDefinition的加載查看,值為相應的對象實例锣尉。這邊需要指出的一點是刻炒,對于bean定義中具有別名意義的字段,如一定情況下的name以及alias字段自沧,只存在于SimpleAliasRegistry維護的內(nèi)部Map中落蝙,通過遞歸查詢的方式可以從一個給定的別名查找到指定的id。

?如下,DefaultSingletonBeanRegistry維護的Map中存在key為testBean筏勒,value為TestBean的對象,SimpleAliasRegistry維護的Map中存在Key為testBeanAlias1旺嬉,value為testBean的記錄管行。當通過testBeanAlias1查找bean時,會先通過AliasRegistry查找到testBean邪媳,再從通過BeanRegistry查找到對應的Bean實例捐顷。

file

?FactoryBeanRegistrySupport:增加緩存FactoryBean實例功能,DefaultSingleBeanRegistry在生成單例后便不再持有對應的FactoryBean

?BeanFactory:定義了Bean容器的基本查詢接口雨效,同時設定了以&前綴來區(qū)別工廠Bean迅涮,即如果beanName前面有&則返回對應Bean的工廠Bean對象而不是該Bean對象。

?HierarchicalBeanFactory:在BeanFactory接口上增加了父子層級關系徽龟,以實現(xiàn)雙親委托叮姑。

?ConfigurableBeanFactory:按照規(guī)矩,增加了修改功能的接口据悔,同時增加了Scope特性传透,默認分為single單例和prototype多例。

?AbstractBeanFactory:BeanFacoty的基本實現(xiàn)极颓。

?AbstractBeanFactory的getBean方法內(nèi)部調(diào)用了doGetBean朱盐,該方法提供了根據(jù)beanName獲取實例的具體實現(xiàn),代碼如下(刪除了相關的注釋和空格):


protected <T> T doGetBean(
            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {

        /*(1)*/
        final String beanName = transformedBeanName(name);
        Object bean;
        /*(2)*/
        Object sharedInstance = getSingleton(beanName);
        /*(3)*/
        if (sharedInstance != null && args == null) {
            if (logger.isDebugEnabled()) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                            "' that is not fully initialized yet - a consequence of a circular reference");
                }
                else {
                    logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }
        /*(4)*/
        else {
            /*(5)*/
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
            /*(6)*/
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                String nameToLookup = originalBeanName(name);
                if (args != null) {
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else {
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
            }
            /*(7)*/
            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }
            try {
                /*(8)*/
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);
                /*(9)*/
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    for (String dep : dependsOn) {
                        if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        registerDependentBean(dep, beanName);
                        try {
                            getBean(dep);
                        }
                        catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                        }
                    }
                }
                /*(10)*/
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            catch (BeansException ex) {
                                destroySingleton(beanName);
                                throw ex;
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
                /*(11)*/
                else if (mbd.isPrototype()) {
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }
                /*(12)*/
                else {
                    String scopeName = mbd.getScope();
                    final Scope scope = this.scopes.get(scopeName);
                    if (scope == null) {
                        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                    }
                    try {
                        Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                            @Override
                            public Object getObject() throws BeansException {
                                beforePrototypeCreation(beanName);
                                try {
                                    return createBean(beanName, mbd, args);
                                }
                                finally {
                                    afterPrototypeCreation(beanName);
                                }
                            }
                        });
                        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    }
                    catch (IllegalStateException ex) {
                        throw new BeanCreationException(beanName,
                                "Scope '" + scopeName + "' is not active for the current thread; consider " +
                                "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                ex);
                    }
                }
            }
            catch (BeansException ex) {
                /*(13)*/
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }
        if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
            try {
                return getTypeConverter().convertIfNecessary(bean, requiredType);
            }
            catch (TypeMismatchException ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Failed to convert bean '" + name + "' to required type '" +
                            ClassUtils.getQualifiedName(requiredType) + "'", ex);
                }
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
        }
        return (T) bean;
    }

先說下入?yún)ⅲ?/p>

  1. name:要查找的bean名菠隆,可以為raw name兵琳,也可以為alias name或者factoryBean name,Spring內(nèi)部會自行進行轉(zhuǎn)換骇径。

  2. requiredType:要返回的對象類型

  3. args:對象實例化時需要用到的構(gòu)造參數(shù)

  4. typeCheckOnly:該對象只是用來進行類型檢查躯肌,而不會真正的進行使用,可以避免實例化和初始化對象

具體過程為:

1.獲取raw name

?計算所給name對應的內(nèi)部beanName既峡,具體為循環(huán)去除name前面的&羡榴,再根據(jù)之前的介紹的,如果傳入的是別名运敢,會查找到對應的raw name

2.嘗試獲取bean實例

?使用上面獲得的beanName校仑,調(diào)用內(nèi)部的getSingleton方法,獲取對應的對象實例传惠,賦值給sharedInstance迄沫。getSingleton方法來自于DefaultSingletonBeanRegistry,即這步嘗試直接從內(nèi)部維護的單例Map中獲取實例卦方。這步可以檢測到手工注入的singleton羊瘩,如第一節(jié)提到的ApplicationContext對象,就是Spring自己手動注冊的。

3.bean實例已經(jīng)存在

?若sharedInstance不為空尘吗,且args參數(shù)為空逝她,說明該對象已經(jīng)存在,不需要再進行實例化和初始化睬捶。由于在(1)的時候?qū)λ鶄鞯膎ame去除了&黔宛,需要判斷返回的對象是否符合要求。這時候擒贸,會使用getObjectForBeanInstance方法臀晃,對sharedInstance和name進行判斷,返回對應的實例介劫,該方法主要內(nèi)容如下:

  1. 若name以&開頭徽惋,但sharedInstance沒有實現(xiàn)FactoryBean接口,則拋出異常

  2. 若sharedInstance沒有實現(xiàn)FactoryBean接口座韵,或者name以&開頭险绘,則直接將sharedInstance對象返回。即sharedInstace本身是從name對應的FactoryBean獲取的對象回右。

  3. 若前面2個條件都不符合隆圆,則sharedInstance本身實現(xiàn)了FactoryBean接口,name也是以&開頭,這時候會嘗試從FactoryBeanRegistrySupport中根據(jù)beanName(raw name)獲取已經(jīng)實例化的對象翔烁。若對象為空渺氧,即首次獲取,則將sharedInstace轉(zhuǎn)為FactoryBean,并調(diào)用該工廠方法獲取對象蹬屹。這里涉及到FactoryBeanRegistrySupport的getObjectFromFactoryBean方法侣背,該方法在使用FactoryBean獲得對象后茫死,會調(diào)用上下文中已有的BeanPostProcessor對象列表染厅,逐個執(zhí)行postProcessAfterInitialization方法,當遇到處理后的結(jié)果為空旨指,則直接返回厦取,否則繼續(xù)遍歷執(zhí)行潮太,如下,出現(xiàn)在AbstractAutowireCapableBeanFactory中:

file

4.Bean實例不存在

?如果沒有找到beanName對應的實例虾攻,即不存在對應的單例實例铡买,則轉(zhuǎn)入實例化該對象的流程,注意單例或者多例都需要實例化霎箍。

5.如果該beanName有對應的在初始化中的多例對象奇钞,則拋出異常。

?AbstractBeanFactory內(nèi)部維護了一個ThreadLocal對象漂坏,用于維護當前線程正在初始化的多例對象景埃。

6.啟用雙親委托機制

?如果存在父容器媒至,且父容器存在該beanName的定義,則委托給父容器完成谷徙。

7.如果本次調(diào)用不單是為了類型檢查拒啰,則標記該beanName在創(chuàng)建中

?AbstractBeanFactory內(nèi)部維護了一個Set<String>集合alreadyCreated,用于存儲已經(jīng)創(chuàng)建好或者正在創(chuàng)建的bean

8.獲取該beanName對應的BeanDefinition,包裝為RootBeanDefinition返回蒂胞。

?AbstractBeanFactory內(nèi)部維護了一個Map<String, RootBeanDefinition>集合mergedBeanDefinitions图呢,用于維護當前已經(jīng)加載的各個bean定義bd。在加載該bean定義時骗随,如果存在父定義pdb,則會將pdb包裝為一個RootBeanDefinition赴叹,然后將當前的bd覆蓋掉父定義的內(nèi)容鸿染,包括scope、lazyInit乞巧、dependsOn等屬性涨椒,達到繼承的效果。獲得RootBeanDefinition后绽媒,如果最后的定義中scope為空蚕冬,則會默認賦值為single。此外還有一個containingBd的概念是辕,這個是相對于bd來說的囤热,指的是包含bd的外部bean定義,主要用于inner bean的情況获三。如果包含containingBd不為空旁蔼,且不是單例,但是bd為單例疙教,則bd的scope需要設置為containingBd的值棺聊,直白點說就是包含被非單例bean包含的bean本身不能為單例(這段有點繞,還沒找到實際的例子贞谓,直接按照代碼里的直譯過來)限佩。

9.處理依賴的bean

?獲取該bean依賴的bean列表dependsOn值,對每個依賴的bean進行逐一操作裸弦,先檢查該bean是否存在循環(huán)依賴祟同,若不存在循環(huán)依賴,則將依賴關系緩存起來烁兰,最后先實例化依賴的bean耐亏。其中檢查循環(huán)依賴很重要,如果沒有該步沪斟,最后實例化依賴的bean時會導致死循環(huán)广辰。為此AbstractBeanFacotry內(nèi)部維護了兩個Map<String, Set<String>>屬性dependentBeanMap和dependenciesForBeanMap暇矫,分別用于緩存bean的依賴關系。前者表示bean從屬關系的緩存择吊,緩存依賴于key所表示的bean的所有bean name李根,舉例來講,如果beanB的一個屬性是beanA,則beanA為key是被依賴方几睛,beanB則為value是依賴方(從屬方)的一員房轿;后者標識bean依賴關系的緩存,緩存key所表示的bean依賴的所有bean name,舉例來講所森,如果beanB的一個屬性是beanA,則beanB是key從屬方囱持,beanA則是value被依賴方的一員。如下為Spring檢查循環(huán)依賴的過程:

file

?其中beanName為當前bean,dependentBeanName為當前bean所依賴的bean焕济。大致過程為找出所有依賴beanName的bean列表transitiveDependency纷妆,遞歸判斷transitiveDependency是否也依賴dependentBeanNam,即如果 beanName依賴于 dependentBeanName 晴弃,而且 transitiveDependency依賴于 beanName, 如果transitiveDependency 依賴于dependentBeanName掩幢,即出現(xiàn)了環(huán),則存在循環(huán)依賴上鞠。

10.如果該bean為單例际邻,則轉(zhuǎn)入初始化單例流程

?調(diào)用父類DefaultSingletonBeanRegistry的getSingleton模板方法,該模板方法會保證該單例只有被創(chuàng)建一次芍阎,創(chuàng)建完成后將對象緩存在內(nèi)部世曾。真正實例化和初始化的過程在createBean方法中,其中如果該bean實例化失敗能曾,則會調(diào)用destroySingleton方法進行回收度硝,這兩個方法在后面會進行重點講解。同第二步類似寿冕,獲取該對象后蕊程,會再調(diào)用getObjectForBeanInstance檢查FactoryBean。

11.如果該bean為多例驼唱,則轉(zhuǎn)入初始化多例流程

?第(5)步講過藻茂,內(nèi)部有一個ThreadLocal,保證多例在當前線程創(chuàng)建時是唯一的玫恳,重點方法也是createBean辨赐。需要注意的是,如果是多例京办,創(chuàng)建失敗是不會進行回收的掀序。

12.如果該bean為其他scope,則轉(zhuǎn)入對應的初始化流程

?具體過程同(10)一致惭婿,只是調(diào)用的模板委托給了具體的Scope對象不恭。

13.初始化失敗叶雹,則清理相關內(nèi)容

?將該beanName從alreadyCreated移除,標識該beanName還未創(chuàng)建换吧。

二折晦、createBean

?createBean方法主要用于完成bean的實例化和初始化過程,該方法在AbstractFactory中為抽象方法沾瓦,具體實現(xiàn)是在AbstractAutowireCapableBeanFactory類中满着。如下為核心操作:

file

1.resolveBeforeInstantiation

?創(chuàng)建對象前的代理口子,能夠攔截創(chuàng)建過程贯莺,使用自定義的代理對象來替換Spring內(nèi)部正常創(chuàng)建的對象风喇,即上面判斷的,如果該方法返回對象不為空缕探,則直接使用返回的對象返回响驴。實現(xiàn)上, 會逐一遍歷所有的BeanPostProcessor撕蔼,找出InstantiationAwareBeanPostProcessor對象,并執(zhí)行postProcessBeforeInstantiation方法秽誊,若返回結(jié)果不為空鲸沮,則直接使用該方法返回,如下:

file

?該方法主要用在AOP實現(xiàn)上锅论,上節(jié)提到的CommonAnnotationBeanPostProcessor和PersistenceAnnotationBeanPostProcessor類雖然實現(xiàn)了該接口讼溺,但是postProcessBeforeInstantiation方法為空實現(xiàn)。

?若該方法返回對象不為空最易,則會逐一執(zhí)行BeanPostProcessor列表的postProcessAfterInitialization方法怒坯,以完成回調(diào)。

2.doCreateBean

?該方法的主要過程如下藻懒,省略了提前暴露bean實例的部分內(nèi)容剔猿。

file

?由上圖可知,該過程完成了bean的實例化和初始化以及調(diào)用各回調(diào)接口的過程嬉荆。具體為:

  1. 根據(jù)BeanDefinition實例化bean

?主要嘗試從各種方法進行實例化归敬,包括:

a. 使用工廠方法進行實例化

b. 使用bean定義的構(gòu)造方法或者可用的構(gòu)造方法進行實例化

c. 使用默認的構(gòu)造方法進行實例化

  1. 回調(diào)MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法

?如下,遍歷各個MergedBeanDefinitionPostProcessor實例鄙早,回調(diào)postProcessMergedBeanDefinition方法

file

3.初始化對象汪茧,填充各屬性

?執(zhí)行初始化,實現(xiàn)屬性的依賴注入限番,在自動進行依賴注入前舱污, 會先調(diào)用一個回調(diào)接口,以判斷是否需要自動依賴注入弥虐,如下:

file

?通過回調(diào)InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法來判斷扩灯。

?若需要進行依賴注入媚赖,則會根據(jù)依賴策略:根據(jù)autowireByName或者autowireByType,為屬性字段找到符合定義的bean實例(會通過getBean方法調(diào)用)驴剔。在真正將值賦值給屬性前省古, 還會再次執(zhí)行回調(diào)接口,如下丧失,回調(diào)InstantiationAwareBeanPostProcessor的postProcessPropertyValues方法豺妓,這里也可以進行攔截。

file

?若前面都沒被攔截到布讹,則會真正將bean值復制給對應的屬性琳拭,最終會通過反射設置field的accessable,然后將bean實例設置進去描验。

4.執(zhí)行各回調(diào)接口

  1. 執(zhí)行Aware接口白嘁,包括BeanNameAware、BeanClassLoaderAware和BeanFactoryAware

  2. 執(zhí)行BeanPostProcessor的postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor類實現(xiàn)了該方法膘流,用以回調(diào)@PostConstruct注解的方法絮缅,CommonAnnotationBeanPostProcessor繼承自該類,設置了initAnnotationType為PostConstruct.class)方法

  3. 如果該Bean實現(xiàn)了InitializingBean接口呼股,則調(diào)用afterPropertiesSet方法

  4. 如果設置了init-method,則執(zhí)行init-method指定的方法

  5. 執(zhí)行BeanPostProcessor的postProcessAfterInitialization方法

5.判斷是否有銷毀接口耕魄,并添加到列表中

?如下,為處理過程彭谁,會先判斷當前bean定義不是多例吸奴,且需要進行銷毀回調(diào),才會進行處理缠局。如果是單例则奥,則直接將其添加到響應列表列表中進行緩存,存儲在內(nèi)部維護的disposableBeans列表中狭园;如果是其他socpe读处,則將其委托給對應的Scope對象實現(xiàn)。

file

?這里有幾個條件:

  1. 必須為非prototy

  2. 該bean存在銷毀方法妙啃,滿足一下條件之一即是

a. 該bean實現(xiàn)了DisposableBean接口

b. 該bean實現(xiàn)了AutoCloseable接口

c.該bean實現(xiàn)了Closeable接口

d.該bean定義的destory-method不為空

e.該bean符合DestructionAwareBeanPostProcessor.requiresDestruction方法的過濾條件

?只要符合以上條件档泽,就會新建一個DisposableBeanAdapter對象進行存儲,并在銷毀時進行相應的接口回調(diào)揖赴。

三馆匿、回調(diào)接口順序

?結(jié)合之前幾節(jié)內(nèi)容,可以得到如下的回調(diào)順序:

file

?以上為大致的過程燥滑,不含其它的回調(diào)接口渐北,若有其它回調(diào)接口可以按照順序依次加入。

file

個人公眾號:啊駝

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铭拧,一起剝皮案震驚了整個濱河市赃蛛,隨后出現(xiàn)的幾起案子恃锉,更是在濱河造成了極大的恐慌,老刑警劉巖呕臂,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件破托,死亡現(xiàn)場離奇詭異,居然都是意外死亡歧蒋,警方通過查閱死者的電腦和手機土砂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谜洽,“玉大人萝映,你說我怎么就攤上這事〔椋” “怎么了序臂?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長实束。 經(jīng)常有香客問我奥秆,道長,這世上最難降的妖魔是什么咸灿? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任吭练,我火速辦了婚禮,結(jié)果婚禮上析显,老公的妹妹穿的比我還像新娘。我一直安慰自己签赃,他們只是感情好谷异,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锦聊,像睡著了一般歹嘹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上孔庭,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天尺上,我揣著相機與錄音,去河邊找鬼圆到。 笑死怎抛,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的芽淡。 我是一名探鬼主播马绝,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼挣菲!你這毒婦竟也來了富稻?” 一聲冷哼從身側(cè)響起掷邦,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎椭赋,沒想到半個月后抚岗,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡哪怔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年宣蔚,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔓涧。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡件已,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出元暴,到底是詐尸還是另有隱情篷扩,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布茉盏,位于F島的核電站鉴未,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鸠姨。R本人自食惡果不足惜铜秆,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望讶迁。 院中可真熱鬧连茧,春花似錦、人聲如沸巍糯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祟峦。三九已至罚斗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宅楞,已是汗流浹背针姿。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留厌衙,地道東北人距淫。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像婶希,于是被迫代替她去往敵國和親溉愁。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

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