Spring Bean 生命周期 (實(shí)例結(jié)合源碼徹底講透)

前言

本篇文章主要是要介紹如何在Spring IoC 容器中 如何管理Spring Bean生命周期型檀。

在應(yīng)用開(kāi)發(fā)中冗尤,常常需要執(zhí)行一些特定的初始化工作,這些工作都是相對(duì)比較固定的胀溺,比如建立數(shù)據(jù)庫(kù)連接裂七,打開(kāi)網(wǎng)絡(luò)連接等,同時(shí)仓坞,在結(jié)束服務(wù)時(shí)背零,也有一些相對(duì)固定的銷(xiāo)毀工作需要執(zhí)行。為了便于這些工作的設(shè)計(jì)无埃,Spring IoC容器提供了相關(guān)的功能徙瓶,可以讓?xiě)?yīng)用定制Bean的初始化和銷(xiāo)毀過(guò)程。

Spring Bean 生命周期

圖片描述

先來(lái)看看 Spring Bean 的生命周期流程圖嫉称。結(jié)合圖看后面的描述會(huì)更輕松一點(diǎn)哦侦镇。

spring bean life cycle.png

文字描述

  1. Bean容器在配置文件中找到Spring Bean的定義。
  2. Bean容器使用Java Reflection API創(chuàng)建Bean的實(shí)例织阅。
  3. 如果聲明了任何屬性壳繁,聲明的屬性會(huì)被設(shè)置。如果屬性本身是Bean蒲稳,則將對(duì)其進(jìn)行解析和設(shè)置氮趋。
  4. 如果Bean類(lèi)實(shí)現(xiàn)BeanNameAware接口,則將通過(guò)傳遞Bean的名稱來(lái)調(diào)用setBeanName()方法江耀。
  5. 如果Bean類(lèi)實(shí)現(xiàn)BeanClassLoaderAware接口剩胁,則將通過(guò)傳遞加載此Bean的ClassLoader對(duì)象的實(shí)例來(lái)調(diào)用setBeanClassLoader()方法。
  6. 如果Bean類(lèi)實(shí)現(xiàn)BeanFactoryAware接口祥国,則將通過(guò)傳遞BeanFactory對(duì)象的實(shí)例來(lái)調(diào)用setBeanFactory()方法昵观。
  7. 如果有任何與BeanFactory關(guān)聯(lián)的BeanPostProcessors對(duì)象已加載Bean,則將在設(shè)置Bean屬性之前調(diào)用postProcessBeforeInitialization()方法舌稀。
  8. 如果Bean類(lèi)實(shí)現(xiàn)了InitializingBean接口啊犬,則在設(shè)置了配置文件中定義的所有Bean屬性后,將調(diào)用afterPropertiesSet()方法壁查。
  9. 如果配置文件中的Bean定義包含init-method屬性觉至,則該屬性的值將解析為Bean類(lèi)中的方法名稱,并將調(diào)用該方法睡腿。
  10. 如果為Bean Factory對(duì)象附加了任何Bean 后置處理器语御,則將調(diào)用postProcessAfterInitialization()方法峻贮。
  11. 如果Bean類(lèi)實(shí)現(xiàn)DisposableBean接口,則當(dāng)Application不再需要Bean引用時(shí)应闯,將調(diào)用destroy()方法纤控。
  12. 如果配置文件中的Bean定義包含destroy-method屬性,那么將調(diào)用Bean類(lèi)中的相應(yīng)方法定義碉纺。

實(shí)例演示

接下來(lái)船万,我們用一個(gè)簡(jiǎn)單的DEMO來(lái)演示一下,整個(gè)生命周期的流轉(zhuǎn)過(guò)程骨田,加深你的印象耿导。

  1. 定義一個(gè)Person類(lèi),實(shí)現(xiàn)了DisposableBean, InitializingBean, BeanFactoryAware, BeanNameAware這4個(gè)接口盛撑,同時(shí)還有自定義的init-methoddestroy-method碎节。這里捧搞,如果不了解這幾個(gè)接口的讀者抵卫,可以先去看看這幾個(gè)接口的定義。

public class Person implements DisposableBean, InitializingBean, BeanFactoryAware, BeanNameAware {

    private String name;

    Person() {
        System.out.println("Constructor of person bean is invoked!");
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("setBeanFactory method of person is invoked");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("setBeanName method of person is invoked");
    }

    public void init() {
        System.out.println("custom init method of person bean is invoked!");
    }

    //Bean initialization code  equals to
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet method of person bean is invoked!");
    }

    //Bean destruction code
    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean Destroy method of person bean is invoked!");
    }

    public void destroyMethod() {
        System.out.println("custom Destroy method of person bean is invoked!");
    }

}
  1. 定義一個(gè)MyBeanPostProcessor實(shí)現(xiàn)BeanPostProcessor接口胎撇。
public class MyBeanPostProcessor implements BeanPostProcessor {


    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("post Process Before Initialization is invoked");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("post Process after Initialization is invoked");
        return bean;
    }
}

  1. 配置文件介粘,指定init-methoddestroy-method屬性
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="myBeanPostProcessor" class="ric.study.demo.ioc.life_cycle_demo_set.MyBeanPostProcessor" />
    <bean name="personBean" class="ric.study.demo.ioc.life_cycle_demo_set.Person"
          init-method="init" destroy-method="destroyMethod">
        <property name="name" value="Richard Yi" />
    </bean>

</beans>
  1. 啟動(dòng)容器、銷(xiāo)毀容器
public class Main {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config-1.xml");
        ((ClassPathXmlApplicationContext) context).destroy();
    }
}
  1. 輸出
Constructor of person bean is invoked!
setBeanName method of person is invoked
setBeanFactory method of person is invoked
post Process Before Initialization is invoked
afterPropertiesSet method of person bean is invoked!
custom init method of person bean is invoked!
post Process after Initialization is invoked
DisposableBean Destroy method of person bean is invoked!
custom Destroy method of person bean is invoked!

可以看到這個(gè)結(jié)果和我們上面描述的一樣晚树。

源碼解析

下面我們從源碼角度來(lái)看看姻采,上述描述的調(diào)用是如何實(shí)現(xiàn)的。

實(shí)際上如果你看過(guò)我之前的文章 Spring IoC 依賴注入 源碼解析的話爵憎,應(yīng)該知道上述調(diào)用的具體實(shí)現(xiàn)慨亲。

這里相當(dāng)于把相關(guān)部分再拎出來(lái)講一遍。

容器初始化

Spring IoC 依賴注入的階段宝鼓,創(chuàng)建Bean有三個(gè)關(guān)鍵步驟

  1. createBeanInstance() 實(shí)例化
  2. populateBean(); 屬性裝配
  3. initializeBean() 處理Bean初始化之后的各種回調(diào)事件

其中刑棵,initializeBean()負(fù)責(zé)處理Bean初始化后的各種回調(diào)事件。

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                @Override
                public Object run() {
                    invokeAwareMethods(beanName, bean);
                    return null;
                }
            }, getAccessControlContext());
        }
        else {
            // 涉及到的回調(diào)接口點(diǎn)進(jìn)去一目了然愚铡,代碼都是自解釋的
            // BeanNameAware蛉签、BeanClassLoaderAware或BeanFactoryAware
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            // BeanPostProcessor 的 postProcessBeforeInitialization 回調(diào)
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            // init-methods
            // 或者是實(shí)現(xiàn)了InitializingBean接口,會(huì)調(diào)用afterPropertiesSet() 方法
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
        if (mbd == null || !mbd.isSynthetic()) {
            // BeanPostProcessor 的 postProcessAfterInitialization 回調(diào)
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
    }

其中invokeAwareMethods會(huì)先調(diào)用一系列的***Aware接口實(shí)現(xiàn)

private void invokeAwareMethods(final String beanName, final Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            if (bean instanceof BeanClassLoaderAware) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
            }
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
            }
        }
    }

然后再執(zhí)行 BeanPostProcessorpostProcessBeforeInitialization 回調(diào)

    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            result = beanProcessor.postProcessBeforeInitialization(result, beanName);
            if (result == null) {
                return result;
            }
        }
        return result;
    }

然后再調(diào)用 初始化方法沥寥,其中包括 InitializingBeanafterPropertiesSet方法和指定的init-method方法碍舍,

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
            throws Throwable {

        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                        @Override
                        public Object run() throws Exception {
                            ((InitializingBean) bean).afterPropertiesSet();
                            return null;
                        }
                    }, getAccessControlContext());
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }

        if (mbd != null) {
            String initMethodName = mbd.getInitMethodName();
            if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }

最后再執(zhí)行 BeanPostProcessorpostProcessAfterInitialization 回調(diào)

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            result = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (result == null) {
                return result;
            }
        }
        return result;
    }

好的,到這里我們介紹了Spring 容器初始化過(guò)程Bean加載過(guò)程當(dāng)中的各種回調(diào)實(shí)現(xiàn)邑雅,下面介紹Spring 容器銷(xiāo)毀階段片橡。

容器關(guān)閉

與Bean初始化類(lèi)似,當(dāng)容器關(guān)閉時(shí)淮野,可以看到對(duì)Bean銷(xiāo)毀方法的調(diào)用捧书。銷(xiāo)毀過(guò)程是這樣的狂塘。順著close()-> doClose() -> destroyBeans() -> destroySingletons() -> destroySingleton() -> destroyBean() -> bean.destroy(),會(huì)看到最終調(diào)用Bean的銷(xiāo)毀方法鳄厌。

protected void destroyBean(String beanName, DisposableBean bean) {
        // 忽略

        // Actually destroy the bean now...
        if (bean != null) {
            try {
                bean.destroy();
            }
            catch (Throwable ex) {
                logger.error("Destroy method on bean with name '" + beanName + "' threw an exception", ex);
            }
        }

        // 忽略
    }

這里注意哦荞胡,這個(gè)Bean的類(lèi)型實(shí)際上是DisposableBeanAdapter,DisposableBeanAdapter是管理Spring Bean的銷(xiāo)毀的,實(shí)際上這里運(yùn)用了適配器模式了嚎。再來(lái)看看destroy()的具體方法泪漂。

@Override
    public void destroy() {
        if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
            for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
                processor.postProcessBeforeDestruction(this.bean, this.beanName);
            }
        }

        if (this.invokeDisposableBean) {
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking destroy() on bean with name '" + this.beanName + "'");
            }
            try {
                if (System.getSecurityManager() != null) {
                    AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                        @Override
                        public Object run() throws Exception {
                            ((DisposableBean) bean).destroy();
                            return null;
                        }
                    }, acc);
                }
                else {
                    // 調(diào)用 DisposableBean 的 destroy()方法
                    ((DisposableBean) bean).destroy();
                }
            }
            catch (Throwable ex) {
                String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
                if (logger.isDebugEnabled()) {
                    logger.warn(msg, ex);
                }
                else {
                    logger.warn(msg + ": " + ex);
                }
            }
        }

        if (this.destroyMethod != null) {
            // 調(diào)用 設(shè)置的destroyMethod
            invokeCustomDestroyMethod(this.destroyMethod);
        }
        else if (this.destroyMethodName != null) {
            Method methodToCall = determineDestroyMethod();
            if (methodToCall != null) {
                invokeCustomDestroyMethod(methodToCall);
            }
        }
    }

BeanPostProcessor 是什么時(shí)候注冊(cè)到容器的?

前面只介紹了BeanPostProcessor類(lèi)在 Spring Bean 生命周期中的回調(diào)實(shí)現(xiàn)歪泳,卻沒(méi)有說(shuō)明 BeanPostProcessor 是什么時(shí)候注冊(cè)到容器的萝勤。下面我們來(lái)介紹下。

在Spring IoC 容器初始化的時(shí)候呐伞,容器會(huì)做一些初始化操作敌卓,其中就包括了BeanPostProcessor的register過(guò)程。詳細(xì)的過(guò)程可以看我這篇IoC 容器初始化伶氢。

這里直接放源碼吧趟径。

源碼位置AbstractApplicationContext#refresh()

@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                // 在這里
                registerBeanPostProcessors(beanFactory);
            // ....忽略
        }
    }
    protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
    }

源碼位置PostProcessorRegistrationDelegate#registerBeanPostProcessors()

public static void registerBeanPostProcessors(
            ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

        String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// step1
        // Register BeanPostProcessorChecker that logs an info message when
        // a bean is created during BeanPostProcessor instantiation, i.e. when
        // a bean is not eligible for getting processed by all BeanPostProcessors.
        int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
        beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

// step2
        // Separate between BeanPostProcessors that implement PriorityOrdered,
        // Ordered, and the rest.
        List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
        List<BeanPostProcessor> internalPostProcessors = new ArrayList<BeanPostProcessor>();
        List<String> orderedPostProcessorNames = new ArrayList<String>();
        List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
        for (String ppName : postProcessorNames) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
                priorityOrderedPostProcessors.add(pp);
                if (pp instanceof MergedBeanDefinitionPostProcessor) {
                    internalPostProcessors.add(pp);
                }
            }
            else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                orderedPostProcessorNames.add(ppName);
            }
            else {
                nonOrderedPostProcessorNames.add(ppName);
            }
        }
// step3
        // First, register the BeanPostProcessors that implement PriorityOrdered.
        sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

        // Next, register the BeanPostProcessors that implement Ordered.
        List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>();
        for (String ppName : orderedPostProcessorNames) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            orderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        sortPostProcessors(orderedPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, orderedPostProcessors);

        // Now, register all regular BeanPostProcessors.
        List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
        for (String ppName : nonOrderedPostProcessorNames) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            nonOrderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

        // Finally, re-register all internal BeanPostProcessors.
        sortPostProcessors(internalPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, internalPostProcessors);

        // Re-register post-processor for detecting inner beans as ApplicationListeners,
        // moving it to the end of the processor chain (for picking up proxies etc).
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
    }

上述過(guò)程可以分成四步:

  1. 通過(guò)beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);方法獲取beanFactory里繼承了BeanPostProcessor接口的name的集合;
  2. 把后置器beans分為PriorityOrdered癣防、Ordered蜗巧、nonOrdered三大類(lèi),前兩類(lèi)是增加了排序條件的后置器蕾盯;(Spring可以通過(guò)PriorityOrderedOrdered接口控制處理器的優(yōu)先級(jí))幕屹,這里實(shí)際上還有一類(lèi)是MergedBeanDefinitionPostProcessor,不是核心點(diǎn)级遭,不展開(kāi)講望拖。
  3. 第三步可以分為以下小步
    1. priorityOrderedPostProcessors,先排序后注冊(cè)
    2. orderedPostProcessors挫鸽,先排序后注冊(cè)
    3. 注冊(cè)nonOrderedPostProcessors说敏,就是一般的處理器
    4. internalPostProcessors,先排序后注冊(cè)
    5. 注冊(cè)一個(gè)ApplicationListenerDetector的 processor

DisposableBeanAdapter 什么時(shí)候注冊(cè)到容器的掠兄?

DisposableBeanAdapter和上文的BeanPostProcessor的抽象層級(jí)不同像云,這個(gè)是和Bean綁定的,所以它的注冊(cè)時(shí)機(jī)是在Spring Bean的依賴注入階段蚂夕,詳細(xì)源碼可以看我的這篇文章Spring IoC 依賴注入 源碼解析迅诬。

源碼位置:AbstractAutowireCapableBeanFactory#doCreateBean()

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
            throws BeanCreationException {
        // 省略前面的超多步驟,想了解的可以去看源碼或者我的那篇文章

        // Register bean as disposable.
        // 這里就是DisposableBeanAdapter的注冊(cè)步驟了
        try {
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
        }

        return exposedObject;
    }

源碼位置:AbstractBeanFactory#registerDisposableBeanIfNecessary()

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
        AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
        if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
            if (mbd.isSingleton()) {
                // 注冊(cè)一個(gè)DisposableBean實(shí)現(xiàn)婿牍,該實(shí)現(xiàn)將執(zhí)行給定bean的所有銷(xiāo)毀工作侈贷。
                // 包括:DestructionAwareBeanPostProcessors,DisposableBean接口,自定義destroy方法俏蛮。
                registerDisposableBean(beanName,
                        new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
            }
            else {
                // A bean with a custom scope...
                Scope scope = this.scopes.get(mbd.getScope());
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
                }
                scope.registerDestructionCallback(beanName,
                        new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
            }
        }
    }

結(jié)語(yǔ)

至此撑蚌,Spring Bean的整個(gè)生命周期算是講解完了,從容器初始化到容器銷(xiāo)毀搏屑,以及回調(diào)事件的注冊(cè)時(shí)機(jī)等方面都說(shuō)明了一下争涌,希望能對(duì)你有所幫助。

本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布辣恋!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末亮垫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子伟骨,更是在濱河造成了極大的恐慌饮潦,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件携狭,死亡現(xiàn)場(chǎng)離奇詭異继蜡,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)逛腿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)稀并,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人鳄逾,你說(shuō)我怎么就攤上這事稻轨×榱” “怎么了雕凹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)政冻。 經(jīng)常有香客問(wèn)我枚抵,道長(zhǎng),這世上最難降的妖魔是什么明场? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任汽摹,我火速辦了婚禮,結(jié)果婚禮上苦锨,老公的妹妹穿的比我還像新娘逼泣。我一直安慰自己,他們只是感情好舟舒,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布拉庶。 她就那樣靜靜地躺著,像睡著了一般秃励。 火紅的嫁衣襯著肌膚如雪氏仗。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,328評(píng)論 1 310
  • 那天夺鲜,我揣著相機(jī)與錄音皆尔,去河邊找鬼呐舔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛慷蠕,可吹牛的內(nèi)容都是我干的珊拼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼流炕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼杆麸!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起浪感,我...
    開(kāi)封第一講書(shū)人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤昔头,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后影兽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體揭斧,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年峻堰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了讹开。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捐名,死狀恐怖旦万,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情镶蹋,我是刑警寧澤成艘,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站贺归,受9級(jí)特大地震影響淆两,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拂酣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一秋冰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧婶熬,春花似錦剑勾、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至性含,卻和暖如春洲赵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工叠萍, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芝发,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓苛谷,卻偏偏與公主長(zhǎng)得像辅鲸,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子腹殿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

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