從零開始開發(fā)一個(gè)Spring Boot Starter

一.Spring Boot Starter簡介

Starter是Spring Boot中的一個(gè)非常重要的概念吭产,Starter相當(dāng)于模塊虚吟,它能將模塊所需的依賴整合起來并對(duì)模塊內(nèi)的Bean根據(jù)環(huán)境( 條件)進(jìn)行自動(dòng)配置娄徊。使用者只需要依賴相應(yīng)功能的Starter,無需做過多的配置和依賴,Spring Boot就能自動(dòng)掃描并加載相應(yīng)的模塊煌寇。
總結(jié):

1.它整合了這個(gè)模塊需要的依賴庫;
2.提供對(duì)模塊的配置項(xiàng)給使用者淑翼;
3.提供自動(dòng)配置類對(duì)模塊內(nèi)的Bean進(jìn)行自動(dòng)裝配腐巢;

例如,在Maven的依賴中加入spring-boot-starter-web就能使項(xiàng)目支持Spring MVC玄括,并且Spring Boot還為我們做了很多默認(rèn)配置冯丙,無需再依賴spring-web、spring-webmvc等相關(guān)包及做相關(guān)配置就能夠立即使用起來遭京。

二.Starter的開發(fā)步驟

編寫Starter非常簡單胃惜,與編寫一個(gè)普通的Spring Boot應(yīng)用沒有太大區(qū)別,總結(jié)如下:

1.新建Maven項(xiàng)目哪雕,在項(xiàng)目的POM文件中定義使用的依賴船殉;
2.新建配置類,寫好配置項(xiàng)和默認(rèn)的配置值斯嚎,指明配置項(xiàng)前綴利虫;
3.新建自動(dòng)裝配類,使用@Configuration和@Bean來進(jìn)行自動(dòng)裝配堡僻;
4.新建spring.factories文件糠惫,指定Starter的自動(dòng)裝配類;

三.Starter的開發(fā)示例

下面苦始,我就以創(chuàng)建一個(gè)自動(dòng)配置并連接ElasticSearch的Starter來講一下各個(gè)步驟及細(xì)節(jié)寞钥。
1.新建Maven項(xiàng)目,在項(xiàng)目的POM文件中定義使用的依賴陌选。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.4.RELEASE</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>es-starter</artifactId>
        <version>1.0.0.SNAPSHORT</version>
    
        <dependencies>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.18</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
                <version>2.0.4.RELEASE</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
    
            <dependency>
                <groupId>org.elasticsearch.client</groupId>
                <artifactId>x-pack-transport</artifactId>
                <version>5.6.4</version>
            </dependency>
        </dependencies>
    </project>

由于本starter主要是與ElasticSearch建立連接理郑,獲得TransportClient對(duì)象,所以需要依賴x-pack-transport包咨油。

2.新建配置類您炉,寫好配置項(xiàng)和默認(rèn)的配置值,指明配置項(xiàng)前綴役电。

package cn.sxw.commons.data.es.starter;

import org.springframework.boot.context.properties.ConfigurationProperties;

import lombok.Data;

/**
 * Created by William on 2018/8/7.
 */
@Data
@ConfigurationProperties(prefix = "sxw.elasticsearch")
public class ElasticSearchProperties {

    private String clusterName = "elasticsearch";

    private String clusterNodes = "127.0.0.1:9300";

    private String userName = "elastic";

    private String password = "changeme";

}

指定配置項(xiàng)前綴為sxw.elasticsearch赚爵,各配置項(xiàng)均有默認(rèn)值,默認(rèn)值可以通過模塊使用者的配置文件進(jìn)行覆蓋法瑟。

3.新建自動(dòng)裝配類冀膝,使用@Configuration@Bean來進(jìn)行自動(dòng)裝配。

package cn.sxw.commons.data.es.starter;

import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import lombok.extern.slf4j.Slf4j;

/**
 * Created by William on 2018/8/7.
 */
@Slf4j
@Configuration
@EnableConfigurationProperties(ElasticSearchProperties.class)
public class ElasticSearchAutoConfiguration implements DisposableBean{

    private TransportClient transportClient;
    @Resource
    private ElasticSearchProperties properties;

    @Bean
    @ConditionalOnMissingBean(TransportClient.class)
    public TransportClient transportClient() {
        log.debug("=======" + properties.getClusterName());
        log.debug("=======" + properties.getClusterNodes());
        log.debug("=======" + properties.getUserName());
        log.debug("=======" + properties.getPassword());
        log.info("開始建立es連接");
        transportClient = new PreBuiltXPackTransportClient(settings());
        TransportAddress[] transportAddresses= Arrays.stream(properties.getClusterNodes().split(",")).map (t->{
            String[] addressPortPairs = t.split(":");
            String address = addressPortPairs[0];
            Integer port = Integer.valueOf(addressPortPairs[1]);
            try {
                return new InetSocketTransportAddress(InetAddress.getByName(address), port);
            } catch (UnknownHostException e) {
                log.error("連接ElasticSearch失敗", e);
                throw new RuntimeException ("連接ElasticSearch失敗",e);
            }
        }).collect (Collectors.toList ()).toArray (new TransportAddress[0]);
        transportClient.addTransportAddresses(transportAddresses);
        return transportClient;
    }

    private Settings settings() {
        return Settings.builder()
                .put("cluster.name", properties.getClusterName())
                .put("xpack.security.user", properties.getUserName() +
                        ":" + properties.getPassword())
                .build();
    }

    @Override
    public void destroy() throws Exception {
        log.info("開始銷毀Es的連接");
        if (transportClient != null) {
            transportClient.close();
        }
    }
}

本類主要對(duì)TransportClient類進(jìn)行自動(dòng)配置;
@ConditionalOnMissingBean 當(dāng)Spring容器中沒有TransportClient類的對(duì)象時(shí)霎挟,調(diào)用transportClient()創(chuàng)建對(duì)象;
關(guān)于更多Bean的條件裝配用法請(qǐng)自行查閱Spring Boot相關(guān)文檔;

4.新建spring.factories文件窝剖,指定Starter的自動(dòng)裝配類。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  cn.sxw.commons.data.es.starter.ElasticSearchAutoConfiguration

spring.factories文件位于resources/META-INF目錄下酥夭,需要手動(dòng)創(chuàng)建;
org.springframework.boot.autoconfigure.EnableAutoConfiguration后面的類名說明了自動(dòng)裝配類赐纱,如果有多個(gè) 脊奋,則用逗號(hào)分開;
使用者應(yīng)用(SpringBoot)在啟動(dòng)的時(shí)候,會(huì)通過org.springframework.core.io.support.SpringFactoriesLoader讀取classpath下每個(gè)Starter的spring.factories文件疙描,加載自動(dòng)裝配類進(jìn)行Bean的自動(dòng)裝配诚隙;

至此,整個(gè)Starter開發(fā)完畢起胰,Deploy到中央倉庫或Install到本地倉庫后即可使用久又。

四.Starter的使用

1.創(chuàng)建Maven項(xiàng)目,依賴剛才發(fā)布的es-starter包待错。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-boot-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.0.4.RELEASE</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>es-example</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>es-starter</artifactId>
            <version>1.0.0.SNAPSHORT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

只需依賴剛才開發(fā)的es-starter即可

2.編寫應(yīng)用程序啟動(dòng)類籽孙。

package cn.sxw.commons.data.es.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

/**
 * Created by William on 2018/8/7.
 */
@SpringBootApplication
@ComponentScan("cn.sxw.commons.data.es.example")
public class ExampleApplication {

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

@SpringBootApplication由@SpringBootConfiguration、@EnableAutoConfiguration火俄、@ComponentScan三個(gè)注解組合而成犯建,其中@EnableAutoConfiguration注解讓Spring Boot根據(jù)類路徑中的jar包依賴為當(dāng)前項(xiàng)目進(jìn)行自動(dòng)配置。

3.編寫查詢ElasticSearch的使用類

package cn.sxw.commons.data.es.example;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.Map;

import lombok.extern.slf4j.Slf4j;

/**
 * Created by William on 2018/8/7.
 */
@Slf4j
@Component
public class ExampleRunner implements ApplicationRunner {

    private static final String INDEX_NAME = "tb_question";

    @Autowired
    private TransportClient transportClient;

    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {
        SearchResponse response = transportClient.prepareSearch(INDEX_NAME)
                .setTypes(INDEX_NAME)
                .setQuery(QueryBuilders.matchAllQuery())
                .setFrom(0).setSize(5).execute().actionGet();
        SearchHits hits = response.getHits();
        log.info(String.format("=======總共找到%d條記錄", hits.getTotalHits()));
        log.info("=======第一頁數(shù)據(jù):");
        for (SearchHit searchHit : hits) {
            Map<String, Object> source = searchHit.getSource();
            String question = source.get("question").toString();
            log.info(question);
        }
    }
}

通過實(shí)現(xiàn)ApplicationRunner或CommandLineRunner接口瓜客,可以實(shí)現(xiàn)應(yīng)用程序啟動(dòng)完成后自動(dòng)運(yùn)行run方法适瓦,達(dá)到測試es-starter模塊目的。
索引名稱tb_question是公司測試環(huán)境ElasticSearch中的索引谱仪,已存在數(shù)據(jù)玻熙。

4.應(yīng)用程序配置

sxw:
  elasticsearch:
    cluster-name: docker-cluster
    cluster-nodes: 192.168.2.180:9300,192.168.2.181:9300
    user-name: elastic
    password: changeme

在application.yml文件中配置es-starter需要的配置信息,這里連接公司測試環(huán)境中的ElasticSearch疯攒。
這里配置的值可以覆蓋es-starter中默認(rèn)值嗦随,也就是之前ElasticSearchProperties文件中的默認(rèn)值。

5.運(yùn)行程序測試

/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/bin/java "-javaagent:/Applications/開發(fā)/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=52434:/Applications/開發(fā)/IntelliJ IDEA.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath cn.sxw.commons.data.es.example.ExampleApplication
objc[2017]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/bin/java (0x1022a24c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x1023254e0). One of the two will be used. Which one is undefined.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.4.RELEASE)

2018-08-08 16:26:43.161  INFO 2017 --- [           main] c.s.c.d.es.example.ExampleApplication    : Starting ExampleApplication on William.local with PID 2017 (/Users/William/Git/sxw-java/es-spring-boot-starter/es-example/target/classes started by William in /Users/William/Git/sxw-java/es-spring-boot-starter)
2018-08-08 16:26:43.167  INFO 2017 --- [           main] c.s.c.d.es.example.ExampleApplication    : No active profile set, falling back to default profiles: default
2018-08-08 16:26:43.365  INFO 2017 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@635eaaf1: startup date [Wed Aug 08 16:26:43 CST 2018]; root of context hierarchy
2018-08-08 16:26:45.078  INFO 2017 --- [           main] s.c.d.e.s.ElasticSearchAutoConfiguration : =======docker-cluster
2018-08-08 16:26:45.079  INFO 2017 --- [           main] s.c.d.e.s.ElasticSearchAutoConfiguration : =======192.168.2.180:9300,192.168.2.181:9300
2018-08-08 16:26:45.081  INFO 2017 --- [           main] s.c.d.e.s.ElasticSearchAutoConfiguration : =======elastic
2018-08-08 16:26:45.081  INFO 2017 --- [           main] s.c.d.e.s.ElasticSearchAutoConfiguration : =======changeme
2018-08-08 16:26:45.082  INFO 2017 --- [           main] s.c.d.e.s.ElasticSearchAutoConfiguration : 開始建立es連接
2018-08-08 16:26:46.200  INFO 2017 --- [           main] o.elasticsearch.plugins.PluginsService   : no modules loaded
2018-08-08 16:26:46.201  INFO 2017 --- [           main] o.elasticsearch.plugins.PluginsService   : loaded plugin [org.elasticsearch.index.reindex.ReindexPlugin]
2018-08-08 16:26:46.202  INFO 2017 --- [           main] o.elasticsearch.plugins.PluginsService   : loaded plugin [org.elasticsearch.join.ParentJoinPlugin]
2018-08-08 16:26:46.202  INFO 2017 --- [           main] o.elasticsearch.plugins.PluginsService   : loaded plugin [org.elasticsearch.percolator.PercolatorPlugin]
2018-08-08 16:26:46.202  INFO 2017 --- [           main] o.elasticsearch.plugins.PluginsService   : loaded plugin [org.elasticsearch.script.mustache.MustachePlugin]
2018-08-08 16:26:46.202  INFO 2017 --- [           main] o.elasticsearch.plugins.PluginsService   : loaded plugin [org.elasticsearch.transport.Netty3Plugin]
2018-08-08 16:26:46.202  INFO 2017 --- [           main] o.elasticsearch.plugins.PluginsService   : loaded plugin [org.elasticsearch.transport.Netty4Plugin]
2018-08-08 16:26:46.202  INFO 2017 --- [           main] o.elasticsearch.plugins.PluginsService   : loaded plugin [org.elasticsearch.xpack.XPackPlugin]
2018-08-08 16:26:49.137  INFO 2017 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-08-08 16:26:49.157  INFO 2017 --- [           main] c.s.c.d.es.example.ExampleApplication    : Started ExampleApplication in 6.6 seconds (JVM running for 7.915)
2018-08-08 16:26:49.215  INFO 2017 --- [           main] c.s.c.data.es.example.ExampleRunner      : =======總共找到907條記錄
2018-08-08 16:26:49.215  INFO 2017 --- [           main] c.s.c.data.es.example.ExampleRunner      : =======第一頁數(shù)據(jù):
2018-08-08 16:26:49.230  INFO 2017 --- [           main] c.s.c.data.es.example.ExampleRunner      : <p>下列詩句朗讀節(jié)奏有錯(cuò)誤的一項(xiàng)是(  )</p>
2018-08-08 16:26:49.230  INFO 2017 --- [           main] c.s.c.data.es.example.ExampleRunner      : <p><span style=";font-family:宋體;color:rgb(0,0,0);font-size:14px"><span style="font-family:宋體">《臥薪嘗膽》這個(gè)故事出自于(</span> A &nbsp;&nbsp;<span style="font-family:宋體">)</span></span></p><p><span style=";font-family:宋體;color:rgb(0,0,0);font-size:14px">A<span style="font-family:宋體">敬尺、司馬遷《史記》 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-family:Times New Roman">B</span><span style="font-family:宋體">枚尼、司馬光 《資治通鑒》</span></span></p><p><span style=";font-family:宋體;color:rgb(0,0,0);font-size:14px">C<span style="font-family:宋體">、孔子 &nbsp;&nbsp;《論語》 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-family:Times New Roman">D</span><span style="font-family:宋體">砂吞、司馬遷《春秋》</span></span></p><p><br/></p>
2018-08-08 16:26:49.230  INFO 2017 --- [           main] c.s.c.data.es.example.ExampleRunner      : <p style="margin-bottom:7px;margin-bottom:auto;vertical-align:middle"><span style=";font-family:&#39;Cambria Math&#39;;font-size:14pxfont-family:宋體,新宋體">填空題</span></p><p style="margin-bottom:7px;margin-bottom:auto;vertical-align:middle"><span style=";font-family:&#39;Cambria Math&#39;;font-size:14pxfont-family:宋體,新宋體">該模式給當(dāng)?shù)貛淼闹饕绊懯?</span><span style=";font-family:&#39;Cambria Math&#39;;font-size:14px"><br/></span><br/></p><p><br/></p>
2018-08-08 16:26:49.231  INFO 2017 --- [           main] c.s.c.data.es.example.ExampleRunner      : <p>下列詞語沒有錯(cuò)別字的一項(xiàng)是(  )</p>
2018-08-08 16:26:49.231  INFO 2017 --- [           main] c.s.c.data.es.example.ExampleRunner      : <p>語文第16題</p>
2018-08-08 16:26:49.232  INFO 2017 --- [       Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@635eaaf1: startup date [Wed Aug 08 16:26:43 CST 2018]; root of context hierarchy
2018-08-08 16:26:49.234  INFO 2017 --- [       Thread-2] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown
2018-08-08 16:26:49.328  INFO 2017 --- [       Thread-2] s.c.d.e.s.ElasticSearchAutoConfiguration : 開始銷毀Es的連接

Process finished with exit code 0

運(yùn)行程序署恍,觀察控制臺(tái)輸出,es-starter成功與ElasticSearch建立連接蜻直,且應(yīng)用程序啟動(dòng)完后ExampleRunner的run方法查詢出5條數(shù)據(jù)盯质。

源代碼參考提供:
從零開始開發(fā)一個(gè)Spring Boot Starter

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市概而,隨后出現(xiàn)的幾起案子呼巷,更是在濱河造成了極大的恐慌,老刑警劉巖赎瑰,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件王悍,死亡現(xiàn)場離奇詭異,居然都是意外死亡乡范,警方通過查閱死者的電腦和手機(jī)配名,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晋辆,“玉大人渠脉,你說我怎么就攤上這事∑考眩” “怎么了芋膘?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長霸饲。 經(jīng)常有香客問我为朋,道長,這世上最難降的妖魔是什么厚脉? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任习寸,我火速辦了婚禮,結(jié)果婚禮上傻工,老公的妹妹穿的比我還像新娘霞溪。我一直安慰自己,他們只是感情好中捆,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布鸯匹。 她就那樣靜靜地躺著,像睡著了一般泄伪。 火紅的嫁衣襯著肌膚如雪殴蓬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天蟋滴,我揣著相機(jī)與錄音染厅,去河邊找鬼。 笑死脓杉,一個(gè)胖子當(dāng)著我的面吹牛糟秘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播球散,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼尿赚,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了蕉堰?” 一聲冷哼從身側(cè)響起凌净,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎屋讶,沒想到半個(gè)月后冰寻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡皿渗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年斩芭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了轻腺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡划乖,死狀恐怖贬养,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情琴庵,我是刑警寧澤误算,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站迷殿,受9級(jí)特大地震影響儿礼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜庆寺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一蚊夫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧懦尝,春花似錦这橙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至撩匕,卻和暖如春鹰晨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背止毕。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來泰國打工模蜡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人扁凛。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓忍疾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谨朝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子卤妒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

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