Spring(七)核心容器 - 鉤子接口

[toc]

前言

Spring 提供了非常多的擴展接口煤裙,官方將這些接口稱之為鉤子算灸,這些鉤子會在特定的時間被回調(diào)癞志,以此來增強 Spring 功能,眾多優(yōu)秀的框架也是通過擴展這些接口漾狼,來實現(xiàn)自身特定的功能重慢,如 SpringBoot、mybatis 等逊躁。

1似踱、Aware 系列接口

Aware 從字面意思理解就是“知道”、“感知”的意思稽煤,是用來獲取 Spring 內(nèi)部對象的接口核芽。Aware 自身是一個頂級接口,它有一系列子接口酵熙,在一個 Bean 中實現(xiàn)這些子接口并重寫里面的 set 方法后轧简,Spring 容器啟動時,就會回調(diào)該 set 方法匾二,而相應的對象會通過方法參數(shù)傳遞進去哮独。我們以其中的 ApplicationContextAware 接口為例拳芙。

ApplicationContextAware

大部分 Aware 系列接口都有一個規(guī)律,它們以對象名稱為前綴皮璧,獲取的就是該對象舟扎,所以 ApplicationContextAware 獲取的對象是 ApplicationContext 。

public interface ApplicationContextAware extends Aware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

ApplicationContextAware 源碼非常簡單悴务,其繼承了 Aware 接口睹限,并定義一個 set 方法,參數(shù)就是 ApplicationContext 對象讯檐,當然羡疗,其它系列的 Aware 接口也是類似的定義。其具體使用方式如下:

public class Test implements ApplicationContextAware {
    
    private ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

在 Spring 啟動過程中别洪,會回調(diào) setApplicationContext 方法叨恨,并傳入 ApplicationContext 對象,之后就可對該對象進行操作蕉拢。其它系列的 Aware 接口也是如此使用特碳。具體的調(diào)用時機會在后面詳細介紹诚亚。

以下是幾種常用的 Aware 接口:

  • BeanFactoryAware:獲取 BeanFactory 對象晕换,它是基礎(chǔ)的容器接口。
  • BeanNameAware:獲取 Bean 的名稱站宗。
  • EnvironmentAware:獲取 Environment 對象闸准,它表示整個的運行時環(huán)境,可以設置和獲取配置屬性梢灭。
  • ApplicationEventPublisherAware:獲取 ApplicationEventPublisher 對象夷家,它是用來發(fā)布事件的。
  • ResourceLoaderAware:獲取 ResourceLoader 對象敏释,它是獲取資源的工具库快。

2、InitializingBean

InitializingBean 是一個可以在 Bean 的生命周期執(zhí)行自定義操作的接口钥顽,凡是實現(xiàn)該接口的 Bean义屏,在初始化階段都可以執(zhí)行自定義的操作。

public interface InitializingBean {

    void afterPropertiesSet() throws Exception;
}

從 InitializingBean 源碼中可以看出它有一個 afterPropertiesSet 方法蜂大,當一個 Bean 實現(xiàn)該接口時闽铐,在 Bean 的初始化階段,會回調(diào) afterPropertiesSet 方法奶浦,其初始化階段具體指 Bean 設置完屬性之后兄墅。

該接口使用方式如下:

@Component
public class Test implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Test 執(zhí)行初始化");
    }
}

定義啟動類:

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

結(jié)果:

...
2020-02-24 08:43:41.435  INFO 26193 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpTraceFilter' to: [/*]
2020-02-24 08:43:41.435  INFO 26193 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'webMvcMetricsFilter' to: [/*]
Test 執(zhí)行初始化
2020-02-24 08:43:41.577  INFO 26193 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2020-02-24 08:43:41.756  INFO 26193 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@23529fee: startup date [Mon Feb 24 08:43:39 CST 2020]; root of context hierarchy
...

最終,afterPropertiesSet 方法被執(zhí)行并打印輸出語句澳叉。

3隙咸、BeanPostProcessor

BeanPostProcessor 和 InitializingBean 有點類似沐悦,也是可以在 Bean 的生命周期執(zhí)行自定義操作,一般稱之為 Bean 的后置處理器五督,不同的是所踊,
BeanPostProcessor 可以在 Bean 初始化前、后執(zhí)行自定義操作概荷,且針對的目標也不同秕岛,InitializingBean 針對的是實現(xiàn) InitializingBean 接口的 Bean,而 BeanPostProcessor 針對的是所有的 Bean误证。

public interface BeanPostProcessor {

    // Bean 初始化前調(diào)用
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    // Bean 初始化后調(diào)用
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

所有的 Bean 在初始化前继薛、后都會回調(diào)接口中的 postProcessBeforeInitialization 和 postProcessAfterInitialization 方法,入?yún)⑹钱斍罢诔跏蓟?Bean 對象和 BeanName愈捅。值得注意的是 Spring 內(nèi)置了非常多的 BeanPostProcessor 遏考,以此來完善自身功能,這部分會在后面文章深入討論蓝谨。

這里通過自定義 BeanPostProcessor 來了解該接口的使用方式:

// 一般自定義的 BeanPostProcessor 命名格式都是以 BeanPostProcessor 為后綴灌具。
@Component
public class TestBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + " 初始化前執(zhí)行操作");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + " 初始化后執(zhí)行操作");
        return bean;
    }
}

啟動類:

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class);
    }
}

結(jié)果:

...
2020-02-24 23:37:08.949  INFO 26615 --- [           main] com.loong.diveinspringboot.test.Main     : No active profile set, falling back to default profiles: default
2020-02-24 23:37:08.994  INFO 26615 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@2133814f: startup date [Mon Feb 24 23:37:08 CST 2020]; root of context hierarchy
2020-02-24 23:37:09.890  INFO 26615 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
org.springframework.context.event.internalEventListenerProcessor 初始化前執(zhí)行操作
org.springframework.context.event.internalEventListenerProcessor 初始化后執(zhí)行操作
org.springframework.context.event.internalEventListenerFactory 初始化前執(zhí)行操作
org.springframework.context.event.internalEventListenerFactory 初始化后執(zhí)行操作
main 初始化前執(zhí)行操作
main 初始化后執(zhí)行操作
test 初始化前執(zhí)行操作
Test 執(zhí)行初始化
test 初始化后執(zhí)行操作
...
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration初始化前執(zhí)行操作
2020-02-24 23:37:13.097  INFO 26615 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2020-02-24 23:37:13.195  INFO 26615 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-02-24 23:37:13.207  INFO 26615 --- [           main] com.loong.diveinspringboot.test.Main     : Started Main in 4.657 seconds (JVM running for 5.078)
...

可以看到,輸出的結(jié)果中不僅包括自定義的 Test譬巫,還包括 Spring 內(nèi)部的 Bean 咖楣。

BeanPostProcessor 使用場景其實非常多,因為它可以獲取正在初始化的 Bean 對象芦昔,然后可以依據(jù)該 Bean 對象做一些定制化的操作诱贿,如:判斷該 Bean 是否為某個特定對象、獲取 Bean 的注解元數(shù)據(jù)等咕缎。事實上珠十,Spring 內(nèi)部也正是這樣使用的,這部分也會在后面章節(jié)詳細討論凭豪。

4焙蹭、BeanFactoryPostProcessor

BeanFactoryPostProcessor 是 Bean 工廠的后置處理器,一般用來修改上下文中的 BeanDefinition嫂伞,修改 Bean 的屬性值孔厉。

public interface BeanFactoryPostProcessor {

    // 入?yún)⑹且粋€ Bean 工廠:ConfigurableListableBeanFactory。該方法執(zhí)行時末早,所有 BeanDefinition 都已被加載烟馅,但還未實例化 Bean。
    // 可以對其進行覆蓋或添加屬性然磷,甚至可以用于初始化 Bean郑趁。
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

BeanFactoryPostProcessor 源碼非常簡單,其提供了一個 postProcessBeanFactory 方法姿搜,當所有的 BeanDefinition 被加載時寡润,該方法會被回調(diào)捆憎。值得注意的是,Spring 內(nèi)置了許多 BeanFactoryPostProcessor 的實現(xiàn)梭纹,以此來完善自身功能躲惰。

這里,我們來實現(xiàn)一個自定義的 BeanFactoryPostProcessor:

@Component
public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        String beanNames[] = beanFactory.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
            System.out.println(beanDefinition);
        }
    }
}

主要是通過 Bean 工廠獲取所有的 BeanDefinition 变抽。

接著啟動程序:

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class);
    }
}

結(jié)果:

2020-02-25 21:46:00.754  INFO 28907 --- [           main] ConfigServletWebServerApplicationContext : ...
2020-02-25 21:46:01.815  INFO 28907 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : ...
Root bean: class [org.springframework.context.annotation.ConfigurationClassPostProcessor]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
Root bean: class [org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
...
2020-02-25 21:46:04.926  INFO 28907 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : ...
2020-02-25 21:46:04.989  INFO 28907 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : ...
2020-02-25 21:46:04.993  INFO 28907 --- [           main] com.loong.diveinspringboot.test.Main     : ...

可以看到础拨,BeanDefinition 正確輸出,里面是一些 Bean 的相關(guān)定義绍载,如:是否懶加載诡宗、Bean 的 Class 以及 Bean 的屬性等。

5击儡、ImportSelector

ImportSelector 是一個較為重要的擴展接口塔沃,通過該接口可動態(tài)的返回需要被容器管理的類,不過一般用來返回外部的配置類阳谍≈瘢可在標注 @Configuration 注解的類中,通過 @Import 導入 ImportSelector 來使用矫夯。

public interface ImportSelector {

    // 方法入?yún)⑹亲⒔獾脑獢?shù)據(jù)對象鸽疾,返回值是類的全路徑名數(shù)組
    String[] selectImports(AnnotationMetadata importingClassMetadata);
}

selectImports 方法返回的是類的全路徑名。

自定義 ImportSelector:

public class TestImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        
        if (importingClassMetadata.hasAnnotation("")) {
            // 判斷是否包含某個注解
        }
        
        // 返回 Test 的全路徑名茧痒,Test 會被放入到 Spring 容器中
        return new String[]{"com.loong.diveinspringboot.test.Test"};
    }
}

selectImports 方法中可以針對通過 AnnotationMetadata 對象進行邏輯判斷肮韧,AnnotationMetadata 存儲的是注解元數(shù)據(jù)信息,根據(jù)這些信息可以動態(tài)的返回需要被容器管理的類名稱旺订。

定義的 Test 類:

public class Test {
    public void hello() {
        System.out.println("Test -- hello");
    }
}

這里,我們沒有對 Test 標注 @Component 注解超燃,所以区拳,Test 不會自動加入到 Spring 容器中。

@SpringBootApplication
@Import(TestImportSelector.class)
public class Main {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication();
        ConfigurableApplicationContext run = springApplication.run(Main.class);
        Test bean = run.getBean(Test.class);
        bean.hello();
    }
}

之后通過 @Import 導入自定義的 TestImportSelector 意乓,前面也說過樱调,@Import 一般配合 @Configuration 使用,而 @SpringBootApplication 中包含了 @Configuration 注解届良。之后笆凌,通過 getBean 方法從容器中獲取 Test 對象,并調(diào)用 hello 方法士葫。

2020-02-26 08:01:41.712  INFO 29546 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2020-02-26 08:01:41.769  INFO 29546 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-02-26 08:01:41.773  INFO 29546 --- [           main] com.loong.diveinspringboot.test.Main     : Started Main in 4.052 seconds (JVM running for 4.534)
Test -- hello

最終乞而,結(jié)果正確輸出。

6慢显、ImportBeanDefinitionRegistrar

該接口和 ImportSelector 類似爪模,也是配合 @Import 使用欠啤,不過 ImportBeanDefinitionRegistrar 更為直接一點,它可以直接把 Bean 注冊到容器中屋灌。

public interface ImportBeanDefinitionRegistrar {

    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}

入?yún)⒊俗⒔庠獢?shù)據(jù)對象 AnnotationMetadata 外洁段,還多了一個 BeanDefinitionRegistry 對象,在前面的文章講過共郭,該對象定義了關(guān)于 BeanDefinition 的一系列的操作祠丝,如:注冊、移除除嘹、查詢等纽疟。

自定義 ImportBeanDefinitionRegistrar:

public class TestRegistrar implements ImportBeanDefinitionRegistrar {
    // 一般通過 AnnotationMetadata 進行業(yè)務判斷,然后通過 BeanDefinitionRegistry 直接注冊 Bean
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(Test.class);
        beanDefinition.setLazyInit(true);
        registry.registerBeanDefinition(Test.class.getName(), beanDefinition);
    }
}

這里憾赁,主要通過 BeanDefinitionRegistry 手動注冊 Test 類的 BeanDefinition污朽,并設置懶加載屬性。

ImportSelector 和 ImportBeanDefinitionRegistrar 是實現(xiàn) @Enable 模式注解的核心接口龙考,而 @Enable 模式注解在 Spring蟆肆、SpringBoot、SpringCloud 中被大量使用晦款,其依靠這些注解來實現(xiàn)各種功能及特性炎功,是較為重要的擴展接口,我們會在后面的文章中反復討論缓溅,包括 ImportSelector 和 ImportBeanDefinitionRegistrar 是如何被 Spring 調(diào)用的蛇损、以及一些重要的 @Enable 注解實現(xiàn)。

值得注意的是坛怪,SpringBoot 外部化配置淤齐、自動裝配特性就是通過 @Enable 注解配合 ImportSelector 和 ImportBeanDefinitionRegistrar 接口來實現(xiàn)的,這部分在前面的 SpringBoot 系列的文章中已經(jīng)討論過袜匿,感興趣的同學可自行翻閱更啄。

7、FactoryBean

FactoryBean 也是一種 Bean居灯,不同于普通的 Bean祭务,它是用來創(chuàng)建 Bean 實例的,屬于工廠 Bean怪嫌,不過它和普通的創(chuàng)建不同义锥,它提供了更為靈活的方式,其實現(xiàn)有點類似于設計模式中的工廠模式和修飾器模式岩灭。

Spring 框架內(nèi)置了許多 FactoryBean 的實現(xiàn)拌倍,它們在很多應用如(Spring的AOP、ORM川背、事務管理)及與其它第三框架(ehCache)集成時都有體現(xiàn)贰拿。

public interface FactoryBean<T> {
    // 該方法會返回該 FactoryBean “生產(chǎn)”的對象實例蛤袒,我們需要實現(xiàn)該方法以給出自己的對象實例化邏輯
    T getObject() throws Exception;

    // Bean的類型
    Class<?> getObjectType();

    // 是否是單例
    default boolean isSingleton() {
        return true;
    }
}

自定義 FactoryBean:

@Component
public class TestFactoryBean implements FactoryBean<Test> {
    @Override
    public Test getObject() throws Exception {

        // 這里可以靈活的創(chuàng)建 Bean,如:代理膨更、修飾

        return new Test();
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }
}

Test 類:

public class Test {
    public void hello() {
        System.out.println("Test -- hello");
    }
}

啟動類:

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication();
        ConfigurableApplicationContext run = springApplication.run(Main.class);
        Test bean = (Test) run.getBean("testFactoryBean");
        bean.hello();
    }
}

輸出:

2020-02-27 23:16:00.334  INFO 32234 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-02-27 23:16:00.338  INFO 32234 --- [           main] com.loong.diveinspringboot.test.Main     : Started Main in 3.782 seconds (JVM running for 4.187)
Test -- hello

可以看到妙真,啟動類中 getBean 的參數(shù)是 testFactoryBean ,從這可以看出荚守,當容器中的 Bean 實現(xiàn)了 FactoryBean 后珍德,通過 getBean(String BeanName) 獲取到的 Bean 對象并不是 FactoryBean 的實現(xiàn)類對象,而是這個實現(xiàn)類中的 getObject() 方法返回的對象矗漾。如果想獲取 FactoryBean 的實現(xiàn)類锈候,需通過這種方式:getBean(&BeanName),在 BeanName 之前加上&敞贡。

8泵琳、ApplicationListener

ApplicationListener 是 Spring 實現(xiàn)事件機制的核心接口,屬于觀察者設計模式誊役,一般配合 ApplicationEvent 使用获列。在 Spring 容器啟動過程中,會在相應的階段通過 ApplicationContext 發(fā)布 ApplicationEvent 事件蛔垢,之后所有的 ApplicationListener 會被回調(diào)击孩,根據(jù)事件類型,執(zhí)行不同的操作鹏漆。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

    void onApplicationEvent(E event);
}

在 onApplicationEvent 方法中巩梢,通過 instanceof 判斷 event 的事件類型。

自定義 ApplicationListener:

@Component
public class TestApplicationListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof TestApplicationEvent) {
            TestApplicationEvent testApplicationEvent = (TestApplicationEvent) event;
            System.out.println(testApplicationEvent.getName());
        }
    }
}

當自定義的 TestApplicationListener 被回調(diào)時艺玲,判斷當前發(fā)布的事件類型是否是自定義的 TestApplicationEvent括蝠,如果是則輸出事件名稱。

自定義 TestApplicationEvent:

public class TestApplicationEvent extends ApplicationEvent {

    private String name;

    public TestApplicationEvent(Object source, String name) {
        super(source);
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

啟動類:

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication();
        ConfigurableApplicationContext run = springApplication.run(Main.class);
        run.publishEvent(new TestApplicationEvent(new Main(),"Test 事件"));
    }
}

通過 ApplicationContext 發(fā)布 TestApplicationEvent 事件板驳。當然也可以在業(yè)務代碼中通過 ApplicationContextAware 獲取 ApplicationContext 發(fā)布事件又跛。

結(jié)果:

2020-02-27 08:37:10.972  INFO 30984 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2020-02-27 08:37:11.026  INFO 30984 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-02-27 08:37:11.029  INFO 30984 --- [           main] com.loong.diveinspringboot.test.Main     : Started Main in 3.922 seconds (JVM running for 4.367)
Test 事件

ApplicationListener 也被 SpringBoot 進行擴展,來實現(xiàn)自身特定的事件機制若治。這部分也在前面的文章討論過,感興趣的同學可自行翻閱感混。

最后

Spring 的鉤子接口就介紹到這端幼,值得注意的是,Spring 的許多核心功能也是通過其內(nèi)置的鉤子接口來實現(xiàn)的弧满,特別是一些核心注解婆跑,如:@Component 和 @Bean 的實現(xiàn),這些都會在后面的文章一一討論庭呜。




以上就是本章內(nèi)容滑进,如果文章中有錯誤或者需要補充的請及時提出犀忱,本人感激不盡。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扶关,一起剝皮案震驚了整個濱河市阴汇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌节槐,老刑警劉巖搀庶,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異铜异,居然都是意外死亡哥倔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進店門揍庄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咆蒿,“玉大人,你說我怎么就攤上這事蚂子∥植猓” “怎么了?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵缆镣,是天一觀的道長芽突。 經(jīng)常有香客問我,道長董瞻,這世上最難降的妖魔是什么寞蚌? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮钠糊,結(jié)果婚禮上挟秤,老公的妹妹穿的比我還像新娘。我一直安慰自己抄伍,他們只是感情好艘刚,可當我...
    茶點故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著截珍,像睡著了一般攀甚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上岗喉,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天秋度,我揣著相機與錄音,去河邊找鬼钱床。 笑死荚斯,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播事期,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼滥壕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了兽泣?” 一聲冷哼從身側(cè)響起绎橘,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎撞叨,沒想到半個月后金踪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡牵敷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年胡岔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枷餐。...
    茶點故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡靶瘸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出毛肋,到底是詐尸還是另有隱情怨咪,我是刑警寧澤,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布润匙,位于F島的核電站诗眨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏孕讳。R本人自食惡果不足惜匠楚,卻給世界環(huán)境...
    茶點故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望厂财。 院中可真熱鬧芋簿,春花似錦、人聲如沸璃饱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荚恶。三九已至撩穿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谒撼,已是汗流浹背冗锁。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嗤栓,地道東北人。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像茉帅,于是被迫代替她去往敵國和親叨叙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,566評論 2 349

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