SpringBoot 入門終極篇(三)

到目前為止赵讯,這篇就是關(guān)于SpringBoot入門學(xué)習(xí)的最后一篇文章了,學(xué)完這三篇文章對 SpringBoot 也就有了一個初步的了解赁温。

「 前言 」

本文的主要內(nèi)容:

  • 事務(wù)處理
  • Docker安裝及常用命令
  • 接入Redis緩存及配置Session
  • 整合MongoDB
  • 配置開發(fā)與生產(chǎn)環(huán)境
  • 部署項目到Docker上

「 事務(wù)處理 」

關(guān)于事務(wù)疗琉,可以簡單理解為,當(dāng)執(zhí)行多條數(shù)據(jù)操作時钦椭,能確保每條操作能同時執(zhí)行成功,否則有一條失敗就會回滾前面所有執(zhí)行成功的操作,保證一致性玉凯。下面我們來做一個簡單的例子势腮。

@Service
public class MyUserServices {

    @Resource
    private MyUserMapper userMapper;

    @Transactional(rollbackFor = IllegalArgumentException.class, noRollbackFor = IllegalStateException.class)
    public MyUser insertUser(MyUser user) {

        userMapper.insertUser(user);

        if (userMapper.selectUserByName2(user.getUserName()).size() >= 2) {
            throw new IllegalArgumentException("userName " +user.getUserName() + " is all through exist");
        }

        if (user.getPassword().equals("123456")) {
            throw new IllegalStateException("can't insert password 123456");
        }
        return user;
    }
}

可以看到我們給inserUser方法添加了Transactional注解联贩,里面包含了rollbackFornoRollbackFor兩個屬性漫仆,通過字面上我們可以看出,一個是回滾一個是不回滾泪幌,value值都是接收異常class盲厌。可以這樣理解祸泪,當(dāng)方法中拋出rollbackFor定義的異常則會執(zhí)行事務(wù)回滾吗浩,當(dāng)方法中拋出noRollbackFor定義的異常則不會執(zhí)行事務(wù)回滾。后面有兩個判斷没隘,一個是通過判斷如果插入了兩個相同的用戶名懂扼,則拋出異常進(jìn)行回滾,實際上開發(fā)中不會這樣去做右蒲,這里是為了學(xué)習(xí)事務(wù)處理來做的阀湿,另一個是通過判斷密碼為123456拋出異常但不進(jìn)行回滾。

「 Docker安裝及常用命令 」

Docker是一個開源的應(yīng)用容器引擎瑰妄,基于Go語言并遵從Apache2.0協(xié)議開源陷嘴。可以讓開發(fā)者打包他們的應(yīng)用以及依賴包到一個輕量級间坐、可移植的容器中灾挨,然后發(fā)布到任何流行的Linux機器上,也可以實現(xiàn)虛擬化竹宋。容器是完全使用沙箱機制劳澄,相互之間不會有任何接口(類似iPhone的app),更重要的是容器性能開銷極低。

Docker下載地址:
Mac下載:https://download.docker.com/mac/stable/Docker.dmg
Windows下載:https://download.docker.com/win/stable/Docker%20for%20Windows%20Installer.exe

安裝完之后蜈七,運行下面命令浴骂,驗證是否安裝成功

docker version
# 或者
docker info

常用的docker命令如下:

# 搜索image
docker search [imageNmae]
# 拉取 image
docker pull [imageName]
# 列出本機所有 image 
docker image ls 
docker images
# 查看 image 信息
docker images [imageName]
# 強制刪除 image 
docker rmi -f [imageId]
# 后臺運行容器
docker run -d [imageName]
# 查看正在運行的容器
docker ps
# 殺掉容器
docker kill [containerId]
# 在運行的容器中執(zhí)行命令
docker exec -it [containerId] [cmd]
# 強制刪除容器
docker rm -f [containerId]

「 接入Redis緩存及配置Session 」

通過Docker添加Redis, 兩行命令完成, pull命令時間可能會比較久,需要耐心等待宪潮。

# 拉取redis鏡像文件
docker pull redis
# 運行redis容器
# -p 6379:6379: 將容器的6379端口映射到主機的6379端口
# -v $PWD/data:/data: 將主機中當(dāng)前目錄下的data掛載到容器的/data
# -d redis redis-server --appendonly yes: 在容器中后臺執(zhí)行redis-server啟動命令溯警,并打開redis持久化配置
docker run -p 6379:6379 -v $PWD/data:/data -d redis redis-server --appendonly yes

接下來添加Redis依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置application.yml, 添加spring cache和redis配置, 密碼默認(rèn)為空:

spring:
  cache:
    type: redis
    cache-names: soaic
  redis:
    database: 0
    host: 192.168.0.184
    port: 6379
    password:
    jedis:
      pool:
        #連接池支持的最大連接數(shù)
        max-active: 1000
        #連接池中連接用完時,新的請求等待時間,毫秒
        max-wait: -1ms
        #連接池中最多可空閑maxIdle個連接
        max-idle: 400
        min-idle: 0
    timeout: 1000ms

MyUserServices核心代碼:

@Service
public class MyUserServices {

    @Resource
    private MyUserMapper userMapper;

    @CachePut(value = "user", key = "#user.id")  //如果方法參數(shù)為對象,并且不指定key狡相,需要重寫toString方法
    @Transactional(rollbackFor = IllegalArgumentException.class, noRollbackFor = IllegalStateException.class)
    public MyUser insertUser(MyUser user) {

        userMapper.insertUser(user);
        System.out.println("添加緩存key為"+user.getId());

        if (userMapper.selectUserByName2(user.getUserName()).size() >= 2) {
            throw new IllegalArgumentException("userName " +user.getUserName() + " is all through exist");
        }

        if (user.getPassword().equals("123456")) {
            throw new IllegalStateException("can't insert password 123456");
        }
        return user;
    }

    @Cacheable(value = "user", key="id", condition="#id>0") //不指定key梯轻,默認(rèn)以方法參數(shù)為key
    public MyUser selectUser(Integer id) {
        MyUser user = userMapper.selectUser(id);
        System.out.println("添加緩存key為" + id);
        return user;
    }

    @CacheEvict(value = "user")
    public Integer removeUser(Integer id) {
        Integer result = userMapper.deleteUser(id);
        System.out.println("刪除緩存key為" + id);
        return result;
    }
}

注解@CachePut()表示當(dāng)有緩存刷新緩存,沒有則添加緩存尽棕,有三個屬性喳挑,value為緩存的名稱為application.yml配置的cache-names;key為緩存的key, 如果不指定key,默認(rèn)以方法參數(shù)為key,如果方法參數(shù)為對象伊诵,需要重寫toString方法;condition為緩存的條件单绑,條件為true時才進(jìn)行緩存。
注解@Cacheable()表示當(dāng)有緩存則取緩存曹宴,沒有則添加緩存搂橙,三個屬性和注解@CachePut()屬性一致。
注解@CacheEvict()表示當(dāng)有緩存則清除緩存笛坦,除了前面說的三個屬性外区转,還多出兩個屬性,allEntries為true清除所有元素版扩;beforeInvocation為true時表示在調(diào)用該方法之前清除緩存中的指定元素废离,默認(rèn)是方法成功執(zhí)行之后觸發(fā),即方法如果因為拋出異常而未能成功返回時也不會觸發(fā)清除操作礁芦。

接著添加RedisCacheConfig配置key生成規(guī)則蜻韭、配置RedisTemplate和緩存序列化以json格式存儲

@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {
    /**
     * 配置redis key
     */
    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            for (Object obj : params) {
                sb.append(":");
                sb.append(obj.toString());
            }
            return sb.toString();
        };
    }

    /**
     * RedisTemplate 序列化
     */
    @Bean
    public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {

        Jackson2JsonRedisSerializer redisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        redisSerializer.setObjectMapper(objectMapper);

        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setValueSerializer(redisSerializer);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }

    /**
     * redis 緩存序列化
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory){
        Jackson2JsonRedisSerializer<Object> redisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        redisSerializer.setObjectMapper(objectMapper);

        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer));

        return RedisCacheManager.builder(connectionFactory).cacheDefaults(cacheConfiguration).build();
    }
}

最后把MyUser實現(xiàn)Serializable, 否則會報序列化錯誤

public class MyUser implements Serializable {
    ...
}

測試。先通過Controller(這里沒有寫出代碼柿扣,可以到github上查看)調(diào)用MyUserServices中方法肖方,然后進(jìn)入docker運行的容器查看

# 查看正在運行的容器ID
docker ps
# 進(jìn)入redis容器中
docker exec -it [containerId] redis-cli
# 進(jìn)入之后輸入 info 可以查看redis信息
info
# 查看所有key
keys *
# 查看某個key的值
get key

接下來就是配置Session了。為什么要用redis配置session窄刘?有這么幾點原因:
1窥妇、方便管理session
2、托管到redis共享Session方便使用集群, 即一臺服務(wù)器壞了娩践,切換到另一臺服務(wù)器活翩,無需再次登錄還能正常訪問

首先添加依賴

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

要注意一下的是,如果spring-boot-starter-parent版本是2.1.5.RELEASE時翻伺,添加依賴后運行會報下面這個錯誤材泄,解決辦法可以把版本降低,改成2.1.4.RELEASE即可吨岭。

java.lang.IllegalStateException: Error processing condition on org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration.taskScheduler

配置application.yml, 設(shè)置session存儲方式

spring:
  session:
    store-type: redis

添加RedisSessionConfig配置拉宗,啟用Session, 并設(shè)置失效時間

@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)//原 Spring Boot 的 server.session.timeout 屬性不再生效。
public class RedisSessionConfig {

}

接下來配置Session攔截器以及Login接口中登錄成功設(shè)置session辣辫,上篇文章中介紹的還比較詳細(xì)旦事,這里就簡單的貼一下核心代碼:

SessionInterceptor核心代碼

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("SessionInterceptor preHandle");
    HttpSession session = request.getSession(false);
    if (session != null && session.getAttribute("user") != null) {
        return true;
    } else {
        PrintWriter printWriter = response.getWriter();
        printWriter.write("{code: 501, message:\"not login!\"}");
        return false;
    }
}

MyUserController核心代碼

@RequestMapping(value = "/login", method = RequestMethod.GET)
public ResponseResult<MyUser> login(HttpServletRequest request, String userName, String password) {
    ResponseResult<MyUser> responseResult;
    try {
        List<MyUser> myUser = myUserServices.login(userName, password);
        if (myUser != null && myUser.size() > 0) {
            request.getSession(true).setAttribute("user", myUser.get(0));
            responseResult = new ResponseResult<>(200, "login success", myUser.get(0));
        } else {
            responseResult = new ResponseResult<>(501, "login failure: invalid userName or password", null);
        }
    } catch (Exception e) {
        e.printStackTrace();
        responseResult = new ResponseResult<>(501, "login failure: " + e.getMessage(), null);
    }
    return responseResult;
}

最后測試,當(dāng)?shù)谝粫r間訪問其它接口會報501錯誤急灭,而當(dāng)訪問login登錄成功后姐浮,則會保存session,后面其它接口訪問都可以正常葬馋。這里沒有用多臺服務(wù)器測試卖鲤,只測試了下肾扰,當(dāng)服務(wù)器停止再啟動后,訪問接口都不用再重新登錄蛋逾,session存儲在redis中集晚,不會因為服務(wù)器斷開而消失,這就區(qū)別于不托管redis区匣,session是存儲在內(nèi)存中偷拔,每次停止服務(wù)器再啟動都要重新登錄。

「 整合MongoDB 」

通過Docker添加MongoDB, 同樣兩行命令完成沉颂。

docker pull mongo
# -p 27017:27017: 將容器的27017 端口映射到主機的27017 端口
# -v $PWD/db:/data/db: 將主機中當(dāng)前目錄下的db掛載到容器的/data/db条摸,作為mongo數(shù)據(jù)存儲目錄
docker run -p 27017:27017 -v $PWD/db:/data/db -d mongo

添加MongoDB依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

配置application.yml, 這里用Docker添加的所以沒有賬號密碼悦污,當(dāng)有賬號密碼時铸屉,url格式為:mongodb://user:pwd@ip:port/soaic?maxPoolSize=256; 當(dāng)有多臺數(shù)據(jù)庫時,url格式為:mongodb://user:pwd@ip1:port1,ip2:port2/database?maxPoolSize=512

spring:
    data:
        mongodb:
          uri: mongodb://localhost:27017/soaic?maxPoolSize=256

修改MyUser對象, 在類作用域上添加@Document注解定義為一個文檔,切端,也可以理解為是一張表彻坛,注解@Id定義屬性為ID, 注解@Field定義為存儲表中的字段名

@Document("myUser")
public class MyUser implements Serializable {

    @Id
    private String id;
    private String userName;
    private String password;

    @Field("roles")    //在文檔中的名稱為roles, 以數(shù)組形式存儲
    private Collection<Role> roles = new LinkedHashSet<>();

}

添加 Role 對象

public class Role {

    private String roleName;

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
}

在SpringBoot中對mongoDB操作數(shù)據(jù)有多種方式,如:通過繼承MongoRepository對象調(diào)用定義好的方法, 或者遵從定義方法名的規(guī)則踏枣,或者通過注解@Query實現(xiàn)查詢昌屉,?0為占位符取方法參數(shù)的第一個,依次類推

public interface MyUserRepository extends MongoRepository<MyUser, String> {

    List<MyUser> findByUserName(String name);

    @Query("{'userName': ?0}")
    List<MyUser> withQueryUserName(String name);

}

通過MongoTemplate來實現(xiàn), 下面代碼簡單的實現(xiàn)了增刪改查CRUD

@Service
public class MyUserDaoImpl implements MyUserDAO{

    @Autowired
    MongoTemplate mongoTemplate;

    @Override
    public List<MyUser> findByUserName(String userName) {
        Query query = new Query(Criteria.where("userName").is(userName));
        return mongoTemplate.find(query, MyUser.class);
    }

    @Override
    public MyUser insertUser(MyUser myUser) {
        return mongoTemplate.insert(myUser);
    }

    @Override
    public boolean deleteUser(String id) {
        Query query = new Query(Criteria.where("id").is(id));
        DeleteResult result = mongoTemplate.remove(query, MyUser.class);
        return result.getDeletedCount() > 0;
    }

    @Override
    public boolean updateUser(MyUser myUser) {
        Criteria criteria= Criteria.where("id").is(myUser.getId());
        Update update = new Update();
        if (myUser.getUserName() != null)
            update.set("userName", myUser.getUserName());
        if (myUser.getPassword() != null)
            update.set("password", myUser.getPassword());
        UpdateResult result = mongoTemplate.updateFirst(new Query(criteria), update, MyUser.class);
        return result.getModifiedCount() > 0;
    }
}

通過單元測試對上面的代碼進(jìn)行測試, 在test/java/com.soaic.hellospringboot中創(chuàng)建MongoDBTests, 添加注解@SpringBootTest@RunWith(SpringRunner.class), 測試的方法添加@Test注解茵瀑,然后分別點擊方法左邊的綠色小圖標(biāo) Run Test 即可

@RunWith(SpringRunner.class)
@SpringBootTest
public class MongoDBTests {

    @Autowired
    private MyUserRepository myUserRepository;

    @Autowired
    private MyUserDaoImpl myUserDaoImpl;

    @Test
    public void testSave() {
        MyUser myUser = new MyUser();
        myUser.setUserName("Soaic");
        myUser.setPassword("123456");

        Collection<Role> roles = new LinkedHashSet<>();
        Role role = new Role();
        role.setRoleName("管理員");
        roles.add(role);

        Role role1 = new Role();
        role1.setRoleName("程序員");
        roles.add(role1);
        myUser.setRoles(roles);

        //myUserRepository.save(myUser);
        myUserDaoImpl.insertUser(myUser);
    }

    @Test
    public void testFind() {
        List<MyUser> myUserList = myUserDaoImpl.findByUserName("Soaic");
        System.out.println(JSON.toJSONString(myUserList));
    }

    @Test
    public void testUpdate() {
        List<MyUser> myUserList = myUserRepository.withQueryUserName("Soaic");
        for (MyUser myUser: myUserList) {
            myUser.setPassword("1234567");
            myUserDaoImpl.updateUser(myUser);
        }
    }

    @Test
    public void testDel() {
        List<MyUser> myUserList = myUserRepository.findByUserName("Soaic");
        for (MyUser myUser: myUserList) {
            myUserDaoImpl.deleteUser(myUser.getId());
        }
    }

}

查詢mongoDB數(shù)據(jù)庫數(shù)據(jù)有無變化间驮,可以通過如下命令連接mongoDB查詢

# 連接mongodb
docker run -it mongo mongo --host 172.17.0.1
# 查看所有數(shù)據(jù)庫
show dbs
# 切換數(shù)據(jù)庫
use soaic
# 查看數(shù)據(jù)庫狀態(tài)
db.stats()
# 查詢數(shù)據(jù)庫下所有表
show collections
# 查詢某個表的數(shù)據(jù)
db.collection.find() 

「 配置開發(fā)與生產(chǎn)環(huán)境 」

添加application-dev.yml開發(fā)環(huán)境配置和application-prod.yml生產(chǎn)環(huán)境配置, 如果我們想使用生產(chǎn)環(huán)境马昨,因為程序會默認(rèn)加載application.yml, 所以只需要在里面配置spring.profiles.activeprod即可竞帽,配置為dev則為開發(fā)環(huán)境。

spring:
  profiles:
    active: prod

「 部署項目到Docker上 」

部署到Docker上需要先把項目打包成jar包鸿捧,可以通過idea中的 Maven Projects 找到Lifecycle下的clean 和 package 依次雙擊執(zhí)行(也可以執(zhí)行命令mvn clean package)屹篓,最后就可以在target文件夾下找到hellospringboot-0.0.1-SNAPSHOT.jar

如果不部署到Docker上,可以直接運行下面命令啟動項目

java -jar hellospringboot-0.0.1-SNAPSHOT.jar

如果部署到docker上匙奴,我們需要創(chuàng)建一個Dockerfile文件在項目的根目錄堆巧,里面內(nèi)容如下:

FROM java:8

MAINTAINER Soaic

ADD target/hellospringboot-0.0.1-SNAPSHOT.jar app.jar

EXPOSE 8443
EXPOSE 8088

ENTRYPOINT ["java", "-jar", "/app.jar"]

第一行:基于鏡像為Java, 標(biāo)簽版本為8
第二行:作者Soaic
第三行:將hellospringboot-0.0.1-SNAPSHOT.jar添加到鏡像中,并重命名為app.jar
第四行和第五行:運行鏡像的容器泼菌,監(jiān)聽8443和8088端口
第六行:啟動時運行 java -jar app.jar

接下來編譯鏡像谍肤,在項目根目錄下執(zhí)行下面命令,其中hellospringboot為鏡像名稱哗伯,最后一個"."荒揣,用來指明Dockerfile路徑,表示在當(dāng)前路路徑下笋颤,編譯第一次需要下載java8乳附,后面編譯就不需要下載了

docker build -t hellospringboot .

編譯完成后内地,可以通過下面命令查看及運行項目

# 查看是否有一個image為hellospringboot
docker images
# 后臺運行項目,并映射兩個端口號8443和8088赋除,--name為修改運行容器名稱可加可不加默認(rèn)為image名稱
docker run -d --name hellospringboot -p 8443:8443 -p 8088:8088 hellospringboot

最后可以通過 http://localhost:8088https://localhost:8443 這個兩個地址訪問了阱缓。

本文所有源碼都已放在github上:https://github.com/soaic/HelloSpringBoot

以上就是這篇文章的全部內(nèi)容,希望對大家能有所幫助举农,如有疑問或建議歡迎大家留言交流~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末荆针,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子颁糟,更是在濱河造成了極大的恐慌航背,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棱貌,死亡現(xiàn)場離奇詭異玖媚,居然都是意外死亡,警方通過查閱死者的電腦和手機婚脱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門今魔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人障贸,你說我怎么就攤上這事错森。” “怎么了篮洁?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵涩维,是天一觀的道長。 經(jīng)常有香客問我袁波,道長瓦阐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任锋叨,我火速辦了婚禮垄分,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘娃磺。我一直安慰自己薄湿,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布偷卧。 她就那樣靜靜地躺著豺瘤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪听诸。 梳的紋絲不亂的頭發(fā)上坐求,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機與錄音晌梨,去河邊找鬼桥嗤。 笑死须妻,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的泛领。 我是一名探鬼主播荒吏,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼渊鞋!你這毒婦竟也來了绰更?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤锡宋,失蹤者是張志新(化名)和其女友劉穎儡湾,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體执俩,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡徐钠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了奠滑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丹皱。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡妒穴,死狀恐怖宋税,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情讼油,我是刑警寧澤杰赛,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站矮台,受9級特大地震影響乏屯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瘦赫,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一辰晕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧确虱,春花似錦含友、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至宜咒,卻和暖如春惠赫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背故黑。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工儿咱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留庭砍,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓混埠,卻偏偏與公主長得像逗威,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子岔冀,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,086評論 2 355

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