SpringBoot

springboot 概述

SpringBoot能够快速开发,简化部署,适用于微服务

参考嘟嘟大神SpringBoot系列

1. 什么是SpringBoot? 

SpringBoot是一个基于Spring4而产生的一个微框架;Spring Boot并不是要成为Spring IO平台里面众多“Foundation”层项目的替代者。Spring Boot的目标不在于为已解决的问题域提供新的解决方案,而是为平台带来另一种开发体验,从而简化对这些已有技术的使用。对于已经熟悉Spring生态系统的开发人员来说,Boot是一个很理想的选择,不过对于采用Spring技术的新人来说,Boot提供一种更简洁的方式来使用这些技术;SpringBoot也是使用微服务架构的最好选择。

SpringBoot能解决那些问题?

SpringBoot使编码更简单,使配置更简单,使部署更简单,使监控更简单

2.SpringBoot 精要

Spring将很多魔法带入了Spring应用程序的开发之中,其中最重要的是以下四个核心。

自动配置:针对很多Spring应用程序常见的应用功能,Spring Boot能自动提供相关配置

起步依赖:告诉Spring Boot需要什么功能,它就能引入需要的库。

命令行界面:这是Spring Boot的可选特性,借此你只需写代码就能完成完整的应用程序,无需传统项目构建。

Actuator:让你能够深入运行中的Spring Boot应用程序,一探究竟。

3.SpringBoot能带给我们什么?

从字面意思理解,Boot是引导,SpringBoot能够帮助开发者快速搭建Spring框架;

SpringBoot能够帮助开发者快速启动一个Web容器;

SpringBoot继承了Spring框架的原有优秀基因;

SpringBoot简化了Spring的开发过程;

SpringBoot更多的是使用了Java Config的方式,对Spring进行配置;

SpringBoot更是一个微服务的起点。


配置文件的优先级

application.properties和application.yml文件可以放在以下四个位置(优先级由高到低):

外置,在相对于应用程序运行目录的/congfig子目录里。

外置,在应用程序运行的目录里

内置,在config包内

内置,在Classpath根目录

同样,这个列表按照优先级排序,也就是说,src/main/resources/config下application.properties覆盖src/main/resources下application.properties中相同的属性,如图:

配置文件

此外,如果你在相同优先级位置同时有application.properties和application.yml,那么application.properties里的属性里面的属性就会覆盖application.yml。

Profile-多环境配置

在Spring Boot中多环境配置文件名需要满足application-{profile}.properties的格式(或者.yml),其中{profile}对应你的环境标识,比如:

application-dev.properties:开发环境

application-pro.properties:生产环境

想要使用对应的环境,只需要在application.properties中使用spring.profiles.active属性来设置,值对应上面提到的{profile},这里就是指dev、prod这2个。除了spring.profiles.active来激活一个或者多个profile之外,还可以用spring.profiles.include来叠加profile

spring.profiles.active: dev

spring.profiles.include: prodb,promq

除了可以用profile的配置文件来分区配置我们的环境变量,在代码里,我们还可以直接用@Profile注解来进行配置

启动原理解析

SpringBoot的启动类会用到@SpringBootApplication这个注解

@SpringBootApplication由以下三个注解组成:

@Configuration(@SpringBootConfiguration点开查看发现里面还是应用了@Configuration)

@EnableAutoConfiguration

@ComponentScan

 @Configuration

@Configuration就是JavaConfig形式的Spring Ioc容器的配置类使用的那个@Configuration,SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,这里的启动类标注了@Configuration之后,本身其实也是一个IoC容器的配置类。

XML跟config配置方式的区别:

@Configuration
@Bean
Bean之间的依赖

@ComponentScan

@ComponentScan这个注解在Spring中很重要,它对应XML配置中的元素,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。

我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。

注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。

@EnableAutoConfiguration

@EnableCaching、@EnableMBeanExport等,@EnableAutoConfiguration的理念和做事方式其实一脉相承,简单概括一下就是,借助@Import的支持,收集和注册特定场景相关的bean定义

@EnableScheduling是通过@Import将Spring调度框架相关的bean定义都加载到IoC容器。

@EnableMBeanExport是通过@Import将JMX相关的bean定义加载到IoC容器。

而@EnableAutoConfiguration也是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器,仅此而已!



SpringFactoriesLoader从指定的配置文件META-INF/spring.factories加载配置。配合@EnableAutoConfiguration使用的话,它更多是提供一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的一组@Configuration类。   

所以,@EnableAutoConfiguration自动配置的魔法骑士就变成了:从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。


SpringApplication执行流程

SpringApplication的run方法的主要流程大体可以归纳如下:

1) 如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:

根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。

使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。

使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。

推断并设置main方法的定义类。

2) SpringApplication实例初始化完成并且完成设置后,就开始执行run方法的逻辑了,方法执行伊始,首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。

3) 创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。

4) 遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。

5) 如果SpringApplication的showBanner属性被设置为true,则打印banner。

6) 根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成,然后根据条件决定是否添加ShutdownHook,决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader,当然,最重要的,将之前准备好的Environment设置给创建好的ApplicationContext使用。

7) ApplicationContext创建好之后,SpringApplication会再次借助Spring-FactoriesLoader,查找并加载classpath中所有可用的ApplicationContext-Initializer,然后遍历调用这些ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。

8) 遍历调用所有SpringApplicationRunListener的contextPrepared()方法。

9) 最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。

10) 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。

11) 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。

12) 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。

13) 正常情况下,遍历执行SpringApplicationRunListener的finished()方法、(如果整个过程出现异常,则依然调用所有SpringApplicationRunListener的finished()方法,只不过这种情况下会将异常信息一并传入处理)


Spring MVC自动配置

Spring Boot为Spring MVC提供适用于多数应用的自动配置功能。在Spring默认基础上,自动配置添加了以下特性:

1)引入ContentNegotiatingViewResolver和BeanNameViewResolver beans。

2)对静态资源的支持,包括对WebJars的支持。

3)自动注册Converter,GenericConverter,Formatter beans。

4)对HttpMessageConverters的支持。

5)自动注册MessageCodeResolver。

6)对静态index.html的支持。

7)对自定义Favicon的支持。

如果想全面控制Spring MVC,你可以添加自己的@Configuration,并使用@EnableWebMvc对其注解。如果想保留Spring Boot MVC的特性,并只是添加其他的MVC配置(拦截器,formatters,视图控制器等),你可以添加自己的WebMvcConfigurerAdapter类型的@Bean(不使用@EnableWebMvc注解)


默认资源映射

Spring Boot 默认为我们提供了静态资源处理,使用 WebMvcAutoConfiguration 中的配置各种属性。

建议大家使用Spring Boot的默认配置方式,提供的静态资源映射如下(优先级由高到低):

classpath:/META-INF/resources

classpath:/resources

classpath:/static

classpath:/public

静态资源映射

可以在上面4个路径下都放一张同名的图片,访问一下即可验证。

任意在上面一个路径下面放上index.html,当访问应用根目录http://lcoalhost:8080时,会直接映射到index.html页面。

对应的配置文件配置如下:

# 默认值为 /**

spring.mvc.static-path-pattern=

# 默认值为 classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/

spring.resources.static-locations=这里设置要指向的路径,多个使用英文逗号隔开

我们可以通过修改spring.mvc.static-path-pattern来修改默认的映射,例如我改成/dudu/**,那运行的时候访问 http://lcoalhost:8080/dudu/index.html 才对应到index.html页面。

自定义资源映射addResourceHandlers

如果Spring Boot提供的Sping MVC不符合要求,则可以通过一个配置类(注解有@Configuration的类)加上@EnableWebMvc注解来实现完全自己控制的MVC配置。

当然,通常情况下,Spring Boot的自动配置是符合我们大多数需求的。在你既需要保留Spring Boot提供的便利,有需要增加自己的额外的配置的时候,可以定义一个配置类并继承WebMvcConfigurerAdapter,无需使用@EnableWebMvc注解。

比如,我们想自定义静态资源映射目录的话,只需重写addResourceHandlers方法即可。

自定义静态资源映射目录

通过addResourceHandler添加映射路径,然后通过addResourceLocations来指定路径。我们访问自定义my文件夹中的elephant.jpg 图片的地址为 http://localhost:8080/my/elephant.jpg

如果想指定外部的目录也很简单,直接addResourceLocations指定即可,代码如下:

指定外部目录

拦截器addInterceptors

要实现拦截器功能需要完成以下2个步骤:

1.创建我们自己的拦截器类并实现 HandlerInterceptor 接口

2.重写WebMvcConfigurerAdapter中的addInterceptors方法把自定义的拦截器类添加进

首先,自定义拦截器代码:

自定义拦截器

接着,重写WebMvcConfigurerAdapter中的addInterceptors方法如下:

拦截器


默认日志Logback

Logback是log4j框架的作者开发的新一代日志框架,它效率更高、能够适应诸多的运行环境,同时天然支持SLF4J。

假如maven依赖中添加了spring-boot-starter-logging:

Logback依赖

那么,我们的Spring Boot应用将自动使用logback作为应用日志框架

(但是呢,实际开发中我们不需要直接添加该依赖,你会发现spring-boot-starter其中包含了 spring-boot-starter-logging,该依赖内容就是 Spring Boot 默认的日志框架 logback。而Thymeleaf依赖包含了spring-boot-starter,最终我只要引入Thymeleaf即可)

文件输出

默认情况下,Spring Boot将日志输出到控制台,不会写到日志文件。如果要编写除控制台输出之外的日志文件,则需在application.properties中设置logging.file或logging.path属性。

logging.file,设置文件,可以是绝对路径,也可以是相对路径。如:logging.file=my.log

logging.path,设置目录,会在该目录下创建spring.log文件,并写入日志内容,如:logging.path=/var/log

如果只配置 logging.file,会在项目的当前路径下生成一个 xxx.log 日志文件。

如果只配置 logging.path,在 /var/log文件夹生成一个日志文件为 spring.log

注:二者不能同时使用,如若同时使用,则只有logging.file生效


JdbcTemplate的使用

这里需要添加spring-boot-starter-jdbc依赖跟mysql依赖

添加依赖


在src/main/resources/application.properties中配置数据源信息。

配置数据源


Spring的JdbcTemplate是自动配置的,你可以直接使用@Autowired来注入到你自己的bean中来使用。

在dao层的实现类,注入我们需要的JdbcTemplate。

注入JdbcTemplate


MyBatis的使用

添加mybatis-spring-boot-starter依赖跟mysql依赖,mybatis-spring-boot-starter中已经包含了Jdbc依赖

引入依赖

MyBatis-Spring-Boot-Starter依赖将会提供如下:

自动检测现有的DataSource

将创建并注册SqlSessionFactory的实例,该实例使用SqlSessionFactoryBean将该DataSource作为输入进行传递

将创建并注册从SqlSessionFactory中获取的SqlSessionTemplate的实例。

自动扫描您的mappers,将它们链接到SqlSessionTemplate并将其注册到Spring上下文,以便将它们注入到您的bean中。

就是说,使用了该Starter之后,只需要定义一个DataSource即可(application.properties中可配置),它会自动创建使用该DataSource的SqlSessionFactoryBean以及SqlSessionTemplate。会自动扫描你的Mappers,连接到SqlSessionTemplate,并注册到Spring上下文中。

注解方式(xml方式省略)

Mybatis注解的方式好简单,只要定义一个dao接口,然后sql语句通过注解写在接口方法上。最后给这个接口添加@Mapper注解或者在启动类上添加@MapperScan(“com.dudu.dao”)注解都行。

dao层使用注解方式
@DeleteProvider方法补全

这些可选的 SQL 注解允许你指定一个类名和一个方法在执行时来返回运行 允许创建动态 的 SQL。 基于执行的映射语句, MyBatis 会实例化这个类,然后执行由 provider 指定的方法. 该方法可以有选择地接受参数对象.(In MyBatis 3.4 or later, it’s allow multiple parameters) 属性: type,method。type 属性是类。method 属性是方法名。 注意: 这节之后是对 类的 讨论,它可以帮助你以干净,容于阅读 的方式来构建动态 SQL。

通用Mapper插件

引入了mybatis相关的一些依赖以及generator的配置,这里generator配置文件指向src/main/resources/mybatis-generator.xml文件,mybatis-generator.xml文件用来自动生成表对应的Model,Mapper以及xml,一会儿会提到。(Mybatis Geneator 详解

mybatis-generator.xml文件

通用Mapper都可以极大的方便开发人员,对单表封装了许多通用方法,省掉自己写增删改查的sql。(通用Mapper插件网址)

对应的application.properties配置:

配置文件

这里实现一个自己的接口,继承通用的mapper,关键点就是这个接口不能被扫描到,mapper接口不能放在dao包里。

最后在启动类中通过MapperScan注解指定扫描的mapper路径(dao)

定义mapper接口

IDEA自动生成文件的方法:

自动生成文件

根据mybatis-generator.xml文件,自动生成model,mapper,xml文件

自动生成的文件

MyBatis分页插件

物理分页插件pagehelper,引入依赖

pom中添加依赖


只需在查询list之前使用PageHelper.startPage(int pageNum, int pageSize)方法即可。pageNum是第几页,pageSize是每页多少条。

(Service层的实现类) 进行分页查询

业务逻辑层(service)的实现

正常情况下具体业务是每个模块的service里面定义许多方法,然后mapper中实现。

但通用Mapper在Spring4中的最佳用法是通用的Service(具体了解

定义一个通用接口:

定义通用接口

具体实现通用接口类

实现通用接口

到此基本的增删改查通用service就写好了,具体业务的service就直接继承这个接口即可,也可以添加额外的方法

具体的service

具体实现service(实现类extends BaseService),然后再mapper文件中(我觉得相当于dao)实现sevice自定义的方法。

mapper实现具体方法

在自动生成的xml文件中实现具体方法(相当于dao的实现类):

Mapper.xml


Spring Loaded 实现热部署

Spring Loaded是一个用于在JVM运行时重新加载类文件更改的JVM代理,Spring Loaded允许你动态的新增/修改/删除某个方法/字段/构造方法,同样可以修改作用在类/方法/字段/构造方法上的注解.也可以新增/删除/改变枚举中的值。

Spring Loaded有两种方式实现,分别是Maven引入依赖方式或者添加启动参数方式

Maven依赖方式

Maven依赖方式

如果用IDEA也可以这样启动

idea启动

注意:maven依赖的方式只适合spring-boot:run的启动方式,右键那种方式不行。

出现这个说明配置成功

[INFO] Attaching agents: [C:\Users\tengj\.m2\repository\org\springframework\springloaded\1.2.6.RELEASE\springloaded-1.2.6.RELEASE.jar]

还有种方式是右键运行启动类

首先先下载对应的springloaded-1.2.6.RELEASE.jar,可以去上面提到的官网获取(这里直接引用maven依赖已经下载好的路径)

然后打开下图所示的Edit Configurations配置,在VM options中输入:

-javaagent:C:\Users\tengj\.m2\repository\org\springframework\springloaded\1.2.6.RELEASE\springloaded-1.2.6.RELEASE.jar -noverify

上面2种方式随便选择一种即可,当系统通过 mvn spring-boot:run启动或者 右键application debug启动Java文件时,系统会监视classes文件,当有classes文件被改动时,系统会重新加载类文件,不用重启启动服务。(注:IDEA下需要重新编译文件 Ctrl+Shift+F9或者编译项目 Ctrl+F9 )

还可以使用spring-boot-devtools 方式、JRebel插件方式实现热部署

经过简单的测试,发现大多数情况可以使用热部署,不过还是有一些情况下需要重新启动,不可用的情况如下:

1:对于一些第三方框架的注解的修改,不能自动加载,比如:spring mvc的@RequestMapping 

2:application.properties的修改也不行 

3:log4j的配置文件的修改不能即时生


单元测试

Spring Boot中单元测试类写在在src/test/java目录下,你可以手动创建具体测试类,如果是IDEA,则可以通过IDEA自动创建测试类

最简单的单元测试写法(Service层),顶部只要@RunWith(SpringRunner.class)和SpringBootTest即可,想要执行的时候,鼠标放在对应的方法,右键选择run该方法即可。推荐大家使用该assertThat断言。

有时候需要对Controller层(API)做测试,这时候就得用到MockMvc了,你可以不必启动工程就能测试这些接口。MockMvc实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便。

使用MockMvc的时候需要先用MockMvcBuilders使用构建MockMvc对象,如下

创建MockMvc对象

MockMvc简单的方法

使用MoxkMvc进行测试

1. mockMvc.perform执行一个请求

2. MockMvcRequestBuilders.get(“/user/1”)构造一个请求,Post请求就用.post方法

3. contentType(MediaType.APPLICATION_JSON_UTF8)代表发送端发送的数据格式是application/json;charset=UTF-8

4. accept(MediaType.APPLICATION_JSON_UTF8)代表客户端希望接受的数据类型为application/json;charset=UTF-8

5. session(session)注入一个session,这样拦截器才可以通过

6. ResultActions.andExpect添加执行完成后的断言

7. ResultActions.andExpect(MockMvcResultMatchers.status().isOk())方法看请求的状态响应码是否为200如果不是则抛异常,测试不通过

8. andExpect(MockMvcResultMatchers.jsonPath(“$.author”).value(“嘟嘟MD独立博客”))这里jsonPath用来获取author字段比对是否为嘟嘟      MD独立博客,不是就测试不通过

9. ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息

新断言assertThat使用

JUnit 4.4 结合 Hamcrest 提供了一个全新的断言语法——assertThat。只使用 assertThat 一个断言语句,结合 Hamcrest 提供的匹配符,就可以表达全部的测试思想,。 JUnit 4.4 默认提供一些可读的描述性错误信息

使用asserThat的两个例子:

1.  assertThat( [value], [matcher statement] );

2.  assertThat(s, anyOf(containsString("a"), containsString("b")));    //表示s里面是否包含“”“a”或者“”“b”

单元测试回滚

单元个测试的时候如果不想造成垃圾数据,可以开启事物功能,记在方法或者类头部添加@Transactional注解即可,如下:

测试回滚

这样测试完数据就会回滚了,不会造成垃圾数据。如果想关闭回滚,只要加上@Rollback(false)注解即可。传入一个参数value,默认true即回滚,false不回滚。如果你使用的数据库是Mysql,有时候会发现加了注解@Transactional 也不会回滚,那么你就要查看一下你的默认引擎是不是InnoDB,如果不是就要改成InnoDB,如下图:

修改引擎


微服务

微服务是一种细粒度的SOA(面向服务架构)

SOA特性: 面向服务   松耦合  模块化   分布式计算  平台无关性   集中管理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容