Spring Boot 2.4 Release Notes

本文主要参考官文文档。主要目的方便版本升级使用,对于需要升级部分进行翻译,对于新增特性等后续部分未做处理。

1、Upgrading from Spring Boot 2.3

1.1 版本格式变化

从Spring Boot 2.4开始,对应Spring Cloud版本按照新规则命名,这意味着你应该在build.gradle/pom.xml 文件中升级Spring Boot版本,从2.3.5.RELEASE 到 2.4.0。

1.2 spring-boot-starter-test中移除JUnit 5’s vintage engine

如果你升级到Spring Boot 2.4,看到单元测试中编译报错,类似org.junit.Test等JUnit类,是因为spring-boot-starter-test中移除依赖JUnit 5’s vintage engine。Vintage Engine允许JUnit 5运行JUnit 4编写的单元用例。 如果你不想迁移你的单元测试到JUnit 5,可以添加Vintage Engine依赖。参考如下Maven依赖:

<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

如果你正在使用Gradle,等效的配置如下例所示:

testImplementation("org.junit.vintage:junit-vintage-engine") {
    exclude group: "org.hamcrest", module: "hamcrest-core"
}

1.3 配置文件处理(application properties and YAML files)

spring Boot 2.4已经改变了 application.propertiesapplication.yml 文件的处理方式。如果你只有一个简单的 application.propertiesapplication.yml文件,你的升级应该是无缝的。然而,如果你有一个更复杂的设置(配置文件特定的属性或配置profile激活属性),如果你想使用新特性,你可能需要做一些更改
如果你只想要Spring Boot 2.3兼容的逻辑,你可以在application.propertiesapplication.yml文件中设置一个 spring.config.use-legacy-processing属性为 true

1.4 配置数据导入

配置文件可以指定spring.config.locationspring.config.import(在此版本中引入)导入。如果文件或文件夹不存在,则会报错;如避免报错,可以加上optional:前缀。
例如:例如,spring.config.location=optional:/etc/config/application.properties。如果application.properties文件不存在,则忽略。
如果你想把所有的位置都当作可选的,你可以设置 spring.config.on-not-found=ignoreSpringApplication.setDefaultProperties(…) 或使用系统/环境变量。

1.5 嵌入数据库检测

嵌入式数据库逻辑经过了改进,只在数据库位于内存中时才认为它是嵌入式的。如果您使用基于文件的持久性或H2、HSQL和Derby的服务器模式,则此更改会产生两个后果:

  • 不再设置 sa 用户名。如果你依赖于这种行为,你需要在您的配置中设置spring.datasource.username=sa
  • 这样的数据库不会在启动时被初始化,因为它们不再被认为是嵌入式的。你可以像往常一样使用`spring.datasource.initialization-mode进行调优。

1.6 不在支持用户自定义MongoClientSettings

如果你使用自定义的MongoClientSettings bean,不在支持自动配置方式加载。如果你依赖于这种行为,特别是与嵌入式Mongo的结合,考虑将定制器应用到你自己的bean,如下例所示:

@Bean
public MongoClientSettings userDefinedMongoClientSettings(MongoProperties properties, Environment environment) {
   MongoClientSettings.Builder builder = MongoClientSettings.builder();
   //...
   new MongoPropertiesClientSettingsBuilderCustomizer(properties, environment).customize(builder);
   return builder.build();
}

1.7 Logback配置属性

特定于Logback的Logging属性已被重命名,以反映它们是特定于Logback的事实,以前的名称已被弃用。
以下Spring Boot属性已被更改:

  • logging.pattern.rolling-file-namelogging.logback.rollingpolicy.file-name-pattern
  • logging.file.clean-history-on-startlogging.logback.rollingpolicy.clean-history-on-start
  • logging.file.max-sizelogging.logback.rollingpolicy.max-file-size
  • logging.file.total-size-caplogging.logback.rollingpolicy.total-size-cap
  • logging.file.max-historylogging.logback.rollingpolicy.max-history

它们映射到的系统环境属性也一样:

  • ROLLING_FILE_NAME_PATTERNLOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN
  • LOG_FILE_CLEAN_HISTORY_ON_STARTLOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START
  • LOG_FILE_MAX_SIZELOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE
  • LOG_FILE_TOTAL_SIZE_CAPLOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP
  • LOG_FILE_MAX_HISTORYLOGBACK_ROLLINGPOLICY_MAX_HISTORY

1.8 默认Servlet注册

Spring Boot 2.4将不再注册servlet容器提供的 DefaultServlet 。在大多数应用程序中,它不会被使用,因为Spring MVC的 DispatcherServlet '是唯一需要的servlet。你可以设置server.servlet.register-default-servlettrue 如果你仍然需要默认的servlet。

1.9 默认情况下,HTTP traces不再包含cookie报头

默认情况下,Cookie请求报头和Set-Cookie 响应报头不再包含在HTTP traces中。要恢复Spring Boot 2.3的行为,设置 management.trace.http.includecookies, errors, request-headers, response-headers

1.10 Undertow转发时路径

默认情况下,Undertow在转发请求时保留原始请求URL。这个版本覆盖了默认的Undertow以符合Servlet规范。以前的Undertow默认行为可以通过设置属性server.undertow.preserve-path-on-forward为true来恢复。

1.11 Neo4j

这个版本对Neo4j支持进行了重大改革。spring.data.neo4j.* 中的许多属性已经被移除,Neo4j OGM的支持也被移除。
Neo4j驱动程序的配置是通过spring.neo4j.* 命名空间。虽然URI和来自data 命名空间的基本身份验证仍然以不赞成的方式支持。
有关此更改以及Spring Data Neo4j 6带来的内容的更多详细信息,请查看文档

1.12 Hazelcast 4

此版本升级到Hazelcast 4,同时保持与Hazelcast 3.2.x的兼容性。如果你还没有准备好切换到Hazelcast 4,你可以使用hazelcast.version降级。

1.13 Elasticsearch RestClient

Spring Boot将不再自动配置初级的Elasticsearch RestClient bean。RestHighLevelClient bean仍然是自动配置的。
大多数用户不应该需要使用初级的客户端,也不应该受到此更改的影响。

1.14 R2DBC

R2DBC的核心基础设施已经转移到Spring Framework中的新的模块 spring-r2dbc中。如果您正在使用此基础设施,请确保将已弃用的访问迁移到新的核心支持。

1.15 Flyway

Flyway 7的升级包括回调顺序的一些更改。这将是一个突破性的变化,任何人依赖注册订单,我们支持通过@OrderOrdered
如果您正在使用Flyway 5,请确保在升级到Spring Boot 2.4之前升级到Flyway 6,因为Flyway只对一个特性版本进行模式升级。

1.16 移除maven插件flatten-maven-plugin

Spring Boot的构建不再使用Flatten Maven Plugin (flatten-maven-plugin),它的插件管理也被删除了。如果你依赖Spring Boot的托管版本,你应该添加你自己的插件管理。

1.17 移除maven插件exec-maven-plugin

exec-maven-plugin的版本管理已被删除。如果您正在使用这个插件,请在您的PluginManagement中指定一个版本。

1.18 Spring Boot Gradle Plugin

Spring引导Gradle插件bootJar任务的DSL已经更新,因此mainClass 可以使用Property<String>进行配置。如果你当前使用的是mainClassName,例如:

bootJar {
    mainClassName 'com.example.ExampleApplication'
}

你应该把它改为 mainClass:

bootJar {
    mainClass 'com.example.ExampleApplication'
}

1.19 在集成测试中导出Metrics

@SpringBootTest 不再配置可用的监控系统,只提供内存中的 MeterRegistry。如果您将指标作为集成测试的一部分导出,那么您可以在测试中添加 @AutoConfigureMetrics 以恢复之前的行为。

1.20 Deprecations from Spring Boot 2.2 and 2.3

反映Spring Boot发布兼容策略, Spring Boot 2.2中已弃用的代码已在Spring Boot 2.4\中删除。在Spring Boot 2.3中已弃用的代码仍然保留,并计划在Spring Boot 2.5中删除。

2、New and Noteworthy

Check the configuration changelog for a complete overview of the changes in configuration.

Spring Framework 5.3

Spring Boot 2.4 uses Spring Framework 5.3. The Spring Framework wiki has a what’s new section with details of the new release.

Spring Data 2020.0

Spring Boot 2.4 includes version 2020.0 (code-named Ockham) of the Spring Data release train. See the Spring Data wiki for release details.

Neo4j

This release brings support of reactive repositories and rely on a separate auto-configuration for the Neo4j driver. As a result, it is now possible to use Neo4j with or without Spring Data.

Health checks for Neo4j use the Driver and are available as long as the Neo4j driver is configured.

If you want to use @Transactional with reactive access, you’d need to configure the Neo4jReactiveTransactionManager bean yourself for now.

@Bean(ReactiveNeo4jRepositoryConfigurationExtension.DEFAULT_TRANSACTION_MANAGER_BEAN_NAME)
public ReactiveTransactionManager reactiveTransactionManager(Driver driver,
      ReactiveDatabaseSelectionProvider databaseNameProvider) {
    return new ReactiveNeo4jTransactionManager(driver, databaseNameProvider);
}

R2DBC

A R2dbcEntityTemplate is available to simplify the use of Reactive R2DBC usage through entities

Java 15 Support

Spring Boot 2.4 now fully supports (and is tested against) Java 15. The minimum supported version remains Java 8.

Custom property name support

When using constructor binding the name of the property is derived from the parameter name. This can be a problem if you want to use a java reserved keyword. For such situations, you can now use the @Name annotation, something like:

@ConfigurationProperties(prefix = "sample")
@ConstructorBinding
public class SampleConfigurationProperties {

  private final String importValue;

  public SampleConfigurationProperties(@Name("import") String importValue) {
    this.importValue = importValue;
  }

}

The sample above exposes a sample.import property.

Layered jar enabled by default

This release enables layered jars and include the layertools by default. This should improve the efficiency of generated image using the build pack out-of-the-box and lets you benefit of that feature when crafting custom Dockerfile.

Importing Additional Application Config

As long as you haven’t set spring.config.use-legacy-processing to true, you can now import additional properties and yaml files directly from your main application.properties or application.yml.

You can use the spring.config.import property to specify one or more additional config files that should be imported into the Spring Environment. See this section of the reference guide for more details.

We’ve published a short blog explaining why we’ve made these changes.

Volume Mounted Config Directory Trees

The spring.config.import property can also be used to import configuration trees that are commonly used with Kubernetes. A configuration tree is an alternative way of providing key/value pairs. Each pair is declared in its own file, with the filename forming the property key, and the file contents providing the value.

For a complete example, see the updated reference documentation.

Properties loaded from config trees have trailing newline characters trimmed by default.

Importing Config Files That Have no File Extension

Some Cloud platforms only allow you to volume mount files without a file extension. If you have such a constraint, It’s now possible to import these files by providing a hint to Spring Boot about the content type. For example, spring.config.import=/etc/myconfig[.yaml] will load /etc/myconfig as YAML.

Origin Chains

The Origin interface has been updated with a new getParent() method. This allows us to provide a full origin chain that can show exactly where an item originated from.

For example, you might use spring.config.import in your application.properties to import a second file. The Origin of properties loaded from this second file will have a parent that points back to the orginal import declaration.

You can try this yourself by looking at the output of the actuator/env or actuator/configprops actuator endpoints.

Startup Endpoint

A new startup actuator endpoint is now available that shows information about your applications startup. The endpoint can help you identify beans that are taking longer than expected to start.

This work builds on the application startup tracking feature that was recently added to Spring Framwork 5.3. You can read more about the feature in the Spring Framework reference documentation.

The new actuator API is documented here.

Docker/Buildpack Support

Publishing Images

The Maven plugin spring-boot:build-image goal and Gradle plugin bootBuildImage task now have the ability to publish the generated image to a Docker registry. See the Maven and Gradle plugin documentation for more details on configuring the plugins for publishing images.

Authentication

When using Spring Boot’s buildpack support, you can now use a private authenticated Docker registry for your builder or run image. Both username/password and token based authentication are supported.

The Maven and Gradle documentation have been updated to show the new configuration.

Paketo Buildpack Defaults

The image builder used by the Maven plugin spring-boot:build-image goal and Gradle plugin bootBuildImage task by default has been upgraded to the latest Paketo images. The Paketo image registry has been changed from Google Container Registry to Docker Hub for improved accessibility.

Maven Buildpack Support

The spring-boot:build-image Maven goal now puts all project modules dependencies in the "application" layer. This means that if you have multiple project modules in your build, they will now all end up in the same layer.

The XML schema has also been updated to allow the layer to be customized using new <includeModuleDependencies/> and <excludeModuleDependencies/> elements.

See the updated Maven documentation for details.

Gradle Buildpack Support

The bootBuildImage Gradle task now puts all project modules dependencies in the "application" layer. This means that if you have multiple project modules in your build, they will now all end up in the same layer.

You can also use includeProjectDependencies() and excludeProjectDependencies() in the DSL when customizing layers.

See the updated Gradle documentation for details.

Redis Cache Metrics

If you’re using Redis caching you can now expose cache statistics via Micrometer. Metrics logged include the number puts, gets and deletes as well as hits/misses. The number of pending requests and the lock wait duration are also recorded.

To enable the feature, set spring.cache.redis.enable-statistics to true.

Web Configuration Properties

Properties have been added to support configuration of the web locale and resource locations with either Spring MVC or Spring WebFlux. The new properties are:

  • spring.web.locale

  • spring.web.locale-resolver

  • spring.web.resources.*

A new property has been added to support configuration of the actuators management endpoint with either the servlet or reactive web stack:

  • management.server.base-path

These Spring MVC and servlet specific properties have been deprecated in favor of the new properties that support either web stack:

  • spring.mvc.locale

  • spring.mvc.locale-resolver

  • spring.resources.*

  • management.server.servlet.context-path

Register @WebListeners in a way that allows them to register servlets and filters

Servlet @WebListener classes are now registered in such a way that they may themselves register servlets and filters.

Earlier versions of Spring Boot registered them using a call to javax.servlet.Registration.Dynamic. This meant that the following section of Servlet specification (4.4) applied:

If the ServletContext passed to the ServletContextListener’s contextInitialized method where the ServletContextListener was neither declared in web.xml or web-fragment.xml nor annotated with @WebListener then an UnsupportedOperationException MUST be thrown for all the methods defined in ServletContext for programmatic configuration of servlets, filters and listeners.

As of Spring Boot 2.4, we no longer use dynamic registration and so it’s safe to call event.getServletContext().addServlet(…) and event.getServletContext.addFilter(…) from a ServletContextListener.contextInitialized method.

A side-effect of this change is that the Servlet container now creates the instance of the WebListener and, therefore, dependency injection such as with @Autowired can no longer be used. In such cases, @Component should be used instead.

Slice Test for Cassandra

An additional test slice can be used to test components that rely on Cassandra using @DataCassandraTest. As usual, only Cassandra Repositories and the required infrastructure is configured by default.

Here is an example using Testcontainers and @DynamicPropertSource:

@DataCassandraTest(properties = "spring.data.cassandra.local-datacenter=datacenter1")
@Testcontainers(disabledWithoutDocker = true)
class SampleDataCassandraTestIntegrationTests {

    @Container
    static final CassandraContainer<?> cassandra = new CassandraContainer<>().withStartupAttempts(5)
            .withStartupTimeout(Duration.ofMinutes(2));

    @DynamicPropertySource
    static void cassandraProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.data.cassandra.contact-points",
                () -> cassandra.getHost() + ":" + cassandra.getFirstMappedPort());
    }

    ...

}

Flyway 7

This release upgrades to Flyway 7 which brings some additional properties. For the open source edition we’ve add the following spring.flyway properties:

  • url

  • user

  • password

If you’re using the "teams" edition you can also make use of:

  • cherry-pick

  • jdbc-properties

  • oracle-kerberos-cache-file

  • oracle-kerberos-config-file

  • skip-executing-migrations

Configuration property for H2 Console’s web admin password

A new configuration property, spring.h2.console.settings.web-admin-password, for configuring H2 Console’s web admin password has been introduced. The password controls access to the console’s preferences and tools.

CqlSession-Based Health Indicators for Apache Cassandra

New CqlSession based health indicators, CassandraDriverHealthIndicator and CassandraDriverReactiveHealthIndicator, have been introduced. One of these indicators will be auto-configured when Cassandra’s Java Driver is on the classpath. The existing Spring Data Cassandra-based health indicators have been deprecated.

Filtered Scraping with Prometheus

The Actuator’s Prometheus endpoint, /actuator/prometheus, now supports an includedNames query parameter that can be used to filter the samples that are included in the response. See the Actuator API documentation for further details.

Spring Security SAML Configuration Properties

Properties have been added to allow configuration of a SAML2 relying party registration’s decryption credentials and Assertion Consumer Service (ACS). The properties are under the following headings:

Failure Analyzers

FailureAnalizers will now be considered even if a ApplicationContext was not created. This allows them to also analyze any exceptions thrown during Environment processing.

Note that any analyzer that implements BeanFactoryAware or EnvironmentAware will not be used unless the ApplicationContext was created.

Jar Optimizations

When generating runnable Spring Boot jars, empty starter dependencies will now be automatically removed. Since most starters only provide transitive dependencies, there is little point in packaging them in the final jar.

Spring Boot annotation processors are also removed as well as they are only useful during the build. These are spring-boot-autoconfigure-processor and spring-boot-configuration-processor.

If you have your own starter POMs that contain no code, you can add a Spring-Boot-Jar-Type entry to its MANIFEST.MF with the value "dependencies-starter". If you want to filter out an annotation processor, you can add the same property with the value "annotation-processor".

Miscellaneous

Apart from the changes listed above, there have also been lots of minor tweaks and improvements including:

  • Version of the JVM on which the application is running is now logged on startup.

  • Trailing whitespace is automatically trimmed from the value of logging.config.

  • R2DBC pool support exposes additional configuration properties.

  • Exception handling in LdapTemplate can be configured to ignore certain exceptions.

  • ISO offset date time format is support for MVC and Webflux.

  • Add a configuration property to opt-in for the new PathPatternParser in lieu of AntPathMatcher to parse and match request mapping path patterns.

  • @DurationUnit, @DataSizeUnit, and @PeriodUnit can annotate a constructor parameter using @ConstructorBinding.

  • The auto-configured RabbitConnectionFactory checks for the presence of CredentialsProvider and CredentialsRefreshService.

  • A health group can be defined with only exclusions.

  • AbstractRoutingDataSource can be ignored in health checks using management.health.db.ignore-routing-data-sources.

  • The localEntityIdTemplate of A SAML Relying party can be configured.

  • HTTP traces are measures with nanosecond precision.

  • A dedicated FailureAnalyzer provides a meaningful message when a Liquibase changelog is missing.

  • Netty’s request decoder can be customized using server.netty.* properties.

  • Plugin management for the Liquibase Maven Plugin that is consistent with the Liquibase version a Spring Boot version manages.

  • Basic authentication support for Prometheus PushGateway.

  • Allow selecting Jedis when both Jedis and Lettuce are available using spring.redis.client-type.

  • Allow disabling Redis Cluster dynamic sources refresh using spring.redis.lettuce.cluster.refresh.dynamic-sources.

  • The reference documentation now includes both Properties and YAML for all configuration examples.

  • RSocketServer’s fragment size can now be customized using the spring.rsocket.fragment-size property.

  • The charsets used to by Logback and Log4j logging can now be configured using the properties logging.charset.console and logging.charset.file.

  • Gradle’s configuration cache is supported when using Gradle 6.7 or later to build a Spring Boot application.

Dependency Upgrades

Spring Boot 2.4 moves to new versions of several Spring projects:

Numerous third-party dependencies have also been updated, some of the more noteworthy of which are the following:

Deprecations in Spring Boot 2.4

  • ConfigFileApplicationListener has been deprecated in favor of ConfigDataEnvironmentPostProcessor.

  • SpringApplicationBuilder and SpringApplication methods relating to the contextClass have been deprecated in favor of alternatives that use a contextFactory.

  • Some methods of CloudFoundryVcapEnvironmentPostProcessor have been deprecated to work with EnvironmentPostProcessor updates (these should affect most users).

  • The BuildLog build pack support class has deprecated a few methods and replaced them with alternatives that provide more details.

  • Logback constants in LoggingSystemProperties have been deprecated in favor of LogbackLoggingSystemProperties.

  • The isEagerInitFilters/setEagerInitFilters methods in UndertowServletWebServerFactory have been replaced by isEagerFilterInit/setEagerFilterInit.

  • A few methods from ApplicationEnvironmentPreparedEvent, ApplicationStartingEvent and SpringApplicationRunListener have been deprecated to support BootstrapContext.

  • The BuildLog for buildpack support has been updated to support more data (most users will not directly use this class).

  • Some Spring MVC and servlet specific properties have been deprecated (see the Web Configuration Properties section above).

  • Health indicators using Spring Data Cassandra have been deprecated in favour of the ones that are using the raw driver.

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

推荐阅读更多精彩内容