SpringBoot 参考指南——004

Part IV. SpringBoot特性(Spring Boot features)

本节深入介绍SpringBoot的细节。在这里,您可以了解您可能想要使用和定制的关键特性。如果您还没有这样做,您可能希望阅读“Part II Getting Started”和“Part III Using Spring Boot”,以便对基本知识有一个良好的基础。

23. Spring应用程序(SpringApplication)

SpringApplication类提供了一种方便的方法从main()方法启动Spring应用程序。在许多情况下,您可以委托给 SpringApplication.run静态方法,如下面的例子所示:

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

当您的应用程序启动时,您应该看到类似于以下输出的内容:

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

2013-07-31 00:08:16.117  INFO 56603 --- [           main] o.s.b.s.app.SampleApplication            : Starting SampleApplication v0.1.0 on mycomputer with PID 56603 (/apps/myapp.jar started by pwebb)
2013-07-31 00:08:16.166  INFO 56603 --- [           main] ationConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6e5a8246: startup date [Wed Jul 31 00:08:16 PDT 2013]; root of context hierarchy
2014-03-04 13:09:54.912  INFO 41370 --- [           main] .t.TomcatServletWebServerFactory : Server initialized with port: 8080
2014-03-04 13:09:56.501  INFO 41370 --- [           main] o.s.b.s.app.SampleApplication            : Started SampleApplication in 2.992 seconds (JVM running for 3.658)

默认情况下,会显示INFO级别的消息,包括一些相关的启动细节,例如启动应用程序的用户。如果您需要一个不是INFO的日志级别,您可以设置它,如第26.4节“日志级别”所述.

23.1. 启动失败(Startup Failure)

如果应用程序无法启动,注册的FailureAnalyzers会提供专门的错误消息和修复问题的具体操作。例如,如果您在端口8080上启动一个web应用程序,并且该端口已经在使用,您应该会看到类似于以下消息的内容:

***************************
APPLICATION FAILED TO START
***************************

Description:

Embedded servlet container failed to start. Port 8080 was already in use.

Action:

Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.

Spring Boot提供了许多FailureAnalyzer实现,您可以添加自己的

如果没有故障分析器能够处理异常,您仍然可以显示完整的条件报告,以便更好地理解出错的原因。为此,需要启用debug属性或启用DEBUG loggingorg.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

例如,如果您使用java -jar运行您的应用程序,那么您可以按如下启用debug属性:

$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug

23.2. 自定义 Banner(Customizing the Banner)

可以通过向类路径添加banner.txt文件或将spring.banner.location属性设置为此类文件的位置来更改在start up上打印的Banner。如果文件的编码不是UTF-8,可以设置spring.banner.charset。除了文本文件之外,还可以将banner.gifbanner.jpgbanner.png图像文件添加到类路径或设置spring.banner.image.location属性。

在你的banner.txt文件,您可以使用以下任何一个占位符:

** Table 23.1. Banner variables **

变量 描述
${application.version} MANIFEST.MF中声明的应用程序的版本号。例如,Implementation-Version: 1.0打印为1.0
${application.formatted-version} 应用程序的版本号,如在MANIFEST.MF中声明的版本号和用于显示的格式化版本号(用括号括起来,以v为前缀)。
${spring-boot.version} 您正在使用的SpringBoot版本。例如2.1.1.RELEASE。
${spring-boot.formatted-version} 您正在使用的SpringBoot版本,格式化后用于显示(用括号括起来,前缀为v),例如(v2.1.1.RELEASE)。
${Ansi.NAME} (or ${AnsiColor.NAME}, ${AnsiBackground.NAME}, ${AnsiStyle.NAME}) 其中NAME是ANSI转义码的名称。有关详细信息,请参见AnsiPropertySource
${application.title} MANIFEST.MF中声明的应用程序标题。例如,Implementation-Title: MyApp打印为MyApp

如果希望以编程方式生成Banner,可以使用SpringApplication.setBanner(…​)方法。使用org.springframework.boot.Banner接口并实现您自己的printBanner()方法。

您还可以使用spring.main.banner-mode属性来确定是否必须在System.out(Console)上打印横幅、发送到配置的日志记录器(log)或根本不生成横幅(off)。

打印的Banner被注册为单例bean,名称如:springBootBanner

YAML映射到false,所以如果您想禁用应用程序中的banner,请确保添加引号,如下面的示例所示:

spring:

    main:

        banner-mode: "off"

23.3. 定制SpringApplication(Customizing SpringApplication)

如果SpringApplication的默认设置不符合您的口味,那么您可以创建一个本地实例并对其进行定制。例如,要关闭Banner,你可以这样写:

public static void main(String[] args) {
    SpringApplication app = new SpringApplication(MySpringConfiguration.class);
    app.setBannerMode(Banner.Mode.OFF);
    app.run(args);
}

传递给SpringApplication的构造函数参数是Spring Bean的配置源。在大多数情况下,这些是对@Configuration类的引用,但是它们也可以是对XML配置或应该扫描的包的引用。

也可以通过使用application.properties配置文件来配置SpringApplication。有关详细信息,请参见第24章外部化配置

有关配置选项的完整列表,请参见SpringApplication Javadoc

23.4. 使用构造器API(Fluent Builder API)

如果您需要构建一个ApplicationContext层次结构(具有父/子关系的多个上下文),或者如果您更喜欢使用一个“流畅”的构建器API,那么您可以使用SpringApplicationBuilder

SpringApplicationBuilder允许您将多个方法调用链接在一起,并包括允许您创建层次结构的父方法和子方法,如下面的示例所示:

new SpringApplicationBuilder()
        .sources(Parent.class)
        .child(Application.class)
        .bannerMode(Banner.Mode.OFF)
        .run(args);

在创建ApplicationContext层次结构时存在一些限制。例如,Web组件必须包含在子上下文中,并且父上下文和子上下文都使用相同的环境。有关详细信息,请参见SpringApplicationBuilder Javadoc

23.5. Application Events and Listeners

除了常见的 Spring Framework 事件(如ContextRefreshedEvent)之外,SpringApplication还发送一些其他的应用程序事件。

有些事件实际上是在创建ApplicationContext之前触发的,因此您不能将侦听器注册为@Bean。您可以使用SpringApplication.addListeners(…​)方法或SpringApplicationBuilder.listeners(…​)方法注册它们。

如果希望自动注册这些侦听器,不管应用程序是如何创建的,您都可以向项目添加META-INF/spring.factories文件,并使用org.springframework.context.ApplicationListener键引用侦听器,如下面的示例所示:

org.springframework.context.ApplicationListener=com.example.project.MyListener

当您的应用程序运行时,应用程序事件按以下顺序触发:

  1. ApplicationStartingEvent,在程序运行开始时触发,但在其他处理动作之前(除侦听器和初始化程序的注册外);
  2. ApplicationEnvironmentPreparedEvent,当上下文中使用的环境已知,但在创建上下文之前触发;
  3. ApplicationPreparedEvent,在刷新启动之前,bean定义加载之后触发;
  4. ApplicationStartedEvent,在上下文刷新之后,在调用任何应用程序和命令行运行程序之前触发;
  5. ApplicationReadyEvent,在调用任何应用程序和命令行运行程序之后,都会触发,它表示应用程序已经准备好为请求提供服务;
  6. ApplicationFailedEvent,启动时出现异常时触发。

您通常不需要使用应用程序事件,但是知道它们的存在是很方便的。在内部,Spring Boot使用事件处理各种任务。

应用程序事件是通过使用Spring Framework的事件发布机制发送的。此机制的一部分确保在子上下文中发布到侦听器的事件也会在任何祖先上下文中发布到侦听器。因此,如果您的应用程序使用SpringApplication实例的层次结构,侦听器可能会接收到相同类型的应用程序事件的多个实例。

为了让侦听器区分上下文的事件和后代上下文的事件,它应该请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。可以通过实现ApplicationContextAware注入上下文,如果侦听器是bean,则可以使用@Autowired

23.6. WEB环境(Web Environment)

SpringApplication尝试为您创建正确类型的ApplicationContext。用于确定WebApplicationType的算法相当简单:

  • 如果使用Spring MVC,则使用AnnotationConfigServletWebServerApplicationContext
  • 如果Spring MVC不存在,而Spring WebFlux存在,则使用AnnotationConfigReactiveWebServerApplicationContext
  • 否则,将使用AnnotationConfigApplicationContext

这意味着,如果您在同一个应用程序中使用Spring MVC,并且使用了Spring WebFlux中的WebClient创建了一个实例,默认情况下将使用Spring MVC。您可以通过调用setWebApplicationType(WebApplicationType)轻松地覆盖它。

也可以通过调用setApplicationContextClass(…)来完全控制ApplicationContext类型。

在JUnit测试中使用SpringApplication时,通常需要调用setWebApplicationType(WebApplicationType.NONE)

23.7. 访问应用程序参数(Accessing Application Arguments)

如果需要访问传递给SpringApplication.run(…)的应用程序参数,可以注入org.springframework.boot.ApplicationArguments bean。ApplicationArguments接口提供了对原始String[]参数以及解析optionnon-option参数的访问,如下面的示例所示:

import org.springframework.boot.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;

@Component
public class MyBean {

    @Autowired
    public MyBean(ApplicationArguments args) {
        boolean debug = args.containsOption("debug");
        List<String> files = args.getNonOptionArgs();
        // if run with "--debug logfile.txt" debug=true, files=["logfile.txt"]
    }

}

Spring Boot还在SpringEnvironment中注册了CommandLinePropertySource。这样还可以使用@Value注释注入单个应用程序参数。

23.8. Using the ApplicationRunner or CommandLineRunner

如果您需要在启动SpringApplication之后运行一些特定的代码,那么您可以实现ApplicationRunnerCommandLineRunner接口。这两个接口以相同的方式工作,并提供一个运行方法,该方法在SpringApplication.run(…)完成之前调用。

CommandLineRunner接口作为一个简单的字符串数组提供对应用程序参数的访问,而ApplicationRunner使用前面讨论的ApplicationArguments接口。下面的例子展示了一个带有run方法的CommandLineRunner:

import org.springframework.boot.*;
import org.springframework.stereotype.*;

@Component
public class MyBean implements CommandLineRunner {

    public void run(String... args) {
        // Do something...
    }

}

如果定义了几个CommandLineRunnerApplicationRunnerBean,必须按特定顺序调用,则可以另外实现org.springframework.core.Ordered接口或使用org.springframework.core.annotation.Order注解。

23.9. 应用退出(Application Exit)

每个SpringApplication向JVM注册一个shutdown hook,以确保ApplicationContext在退出时优雅地关闭。可以使用所有标准的Spring生命周期回调(例如DisposableBean接口或@PreDestroy注释)。

此外,如果bean希望在调用SpringApplication.exit()时返回特定的退出码,则可以实现org.springframework.boot.ExitCodeGenerator接口。然后可以将此退出代码传递给System.exit(),将其作为状态代码返回,如下面的示例所示:

@SpringBootApplication
public class ExitCodeApplication {

    @Bean
    public ExitCodeGenerator exitCodeGenerator() {
        return () -> 42;
    }

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

}

另外,ExitCodeGenerator接口也可以由异常实现。当遇到这种异常时,Spring Boot返回由实现的getExitCode()方法提供的退出代码。

23.10. 管理特性(Admin Features)

通过指定spring.application.admin.enabled属性,可以为应用程序启用与管理相关的特性。这在MBeanServer平台上公开了SpringApplicationAdminMXBean。您可以使用这个特性来远程管理您的SpringBoot应用程序。这个特性对于任何服务包装器实现都很有用。

如果您想知道应用程序在哪个HTTP端口上运行,请使用local.server.port的键获取该属性。

** 注意 ** :在启用此功能时要小心,因为MBean公开了一个关闭应用程序的方法。

24. 外部化配置(Externalized Configuration)

24.1. Configuring Random Values

24.2. Accessing Command Line Properties

24.3. Application Property Files

24.4. Profile-specific Properties

24.5. Placeholders in Properties

24.6. Encrypting Properties

24.7. Using YAML Instead of Properties

24.7.1. Loading YAML
24.7.2. Exposing YAML as Properties in the Spring Environment
24.7.3. Multi-profile YAML Documents
24.7.4. YAML Shortcomings

24.8. Type-safe Configuration Properties

24.8.1. Third-party Configuration
24.8.2. Relaxed Binding
24.8.3. Merging Complex Types
24.8.4. Properties Conversion
Converting durations
Converting Data Sizes
24.8.5. @ConfigurationProperties Validation
24.8.6. @ConfigurationProperties vs. @Value

25. Profiles

25.1. Adding Active Profiles

25.2. Programmatically Setting Profiles

25.3. Profile-specific Configuration Files

26. Logging

26.1. Log Format

26.2. Console Output

26.2.1. Color-coded Output

26.3. File Output

26.4. Log Levels

26.5. Log Groups

26.6. Custom Log Configuration

26.7. Logback Extensions

26.7.1. Profile-specific Configuration
26.7.2. Environment Properties

27. JSON

27.1. Jackson

27.2. Gson

27.3. JSON-B

28. Developing Web Applications

28.1. The “Spring Web MVC Framework”

28.1.1. Spring MVC Auto-configuration
28.1.2. HttpMessageConverters
28.1.3. Custom JSON Serializers and Deserializers
28.1.4. MessageCodesResolver
28.1.5. Static Content
28.1.6. Welcome Page
28.1.7. Custom Favicon
28.1.8. Path Matching and Content Negotiation
28.1.9. ConfigurableWebBindingInitializer
28.1.10. Template Engines
28.1.11. Error Handling
Custom Error Pages
Mapping Error Pages outside of Spring MVC
28.1.12. Spring HATEOAS
28.1.13. CORS Support

28.2. The “Spring WebFlux Framework”

28.2.1. Spring WebFlux Auto-configuration
28.2.2. HTTP Codecs with HttpMessageReaders and HttpMessageWriters
28.2.3. Static Content
28.2.4. Template Engines
28.2.5. Error Handling
Custom Error Pages
28.2.6. Web Filters

28.3. JAX-RS and Jersey

28.4. Embedded Servlet Container Support

28.4.1. Servlets, Filters, and listeners
Registering Servlets, Filters, and Listeners as Spring Beans
28.4.2. Servlet Context Initialization
Scanning for Servlets, Filters, and listeners
28.4.3. The ServletWebServerApplicationContext
28.4.4. Customizing Embedded Servlet Containers
Programmatic Customization
Customizing ConfigurableServletWebServerFactory Directly
28.4.5. JSP Limitations

28.5. Embedded Reactive Server Support

28.6. Reactive Server Resources Configuration

29. Security

29.1. MVC Security

29.2. WebFlux Security

29.3. OAuth2

29.3.1. Client
OAuth2 client registration for common providers
29.3.2. Resource Server
29.3.3. Authorization Server

29.4. Actuator Security

29.4.1. Cross Site Request Forgery Protection

30. Working with SQL Databases

30.1. Configure a DataSource

30.1.1. Embedded Database Support
30.1.2. Connection to a Production Database
30.1.3. Connection to a JNDI DataSource

30.2. Using JdbcTemplate

30.3. JPA and Spring Data JPA

30.3.1. Entity Classes
30.3.2. Spring Data JPA Repositories
30.3.3. Creating and Dropping JPA Databases
30.3.4. Open EntityManager in View

30.4. Spring Data JDBC

30.5. Using H2’s Web Console

30.5.1. Changing the H2 Console’s Path

30.6. Using jOOQ

30.6.1. Code Generation
30.6.2. Using DSLContext
30.6.3. jOOQ SQL Dialect
30.6.4. Customizing jOOQ

31. Working with NoSQL Technologies

31.1. Redis

31.1.1. Connecting to Redis

31.2. MongoDB

31.2.1. Connecting to a MongoDB Database
31.2.2. MongoTemplate
31.2.3. Spring Data MongoDB Repositories
31.2.4. Embedded Mongo

31.3. Neo4j

31.3.1. Connecting to a Neo4j Database
31.3.2. Using the Embedded Mode
31.3.3. Neo4jSession
31.3.4. Spring Data Neo4j Repositories

31.4. Gemfire

31.5. Solr

31.5.1. Connecting to Solr
31.5.2. Spring Data Solr Repositories

31.6. Elasticsearch

31.6.1. Connecting to Elasticsearch by REST clients
31.6.2. Connecting to Elasticsearch by Using Jest
31.6.3. Connecting to Elasticsearch by Using Spring Data
31.6.4. Spring Data Elasticsearch Repositories

31.7. Cassandra

31.7.1. Connecting to Cassandra
31.7.2. Spring Data Cassandra Repositories

31.8. Couchbase

31.8.1. Connecting to Couchbase
31.8.2. Spring Data Couchbase Repositories

31.9. LDAP

31.9.1. Connecting to an LDAP Server
31.9.2. Spring Data LDAP Repositories
31.9.3. Embedded In-memory LDAP Server

31.10. InfluxDB

31.10.1. Connecting to InfluxDB

32. Caching

32.1. Supported Cache Providers

32.1.1. Generic
32.1.2. JCache (JSR-107)
32.1.3. EhCache 2.x
32.1.4. Hazelcast
32.1.5. Infinispan
32.1.6. Couchbase
32.1.7. Redis
32.1.8. Caffeine
32.1.9. Simple
32.1.10. None

33. Messaging

33.1. JMS

33.1.1. ActiveMQ Support
33.1.2. Artemis Support
33.1.3. Using a JNDI ConnectionFactory
33.1.4. Sending a Message
33.1.5. Receiving a Message

33.2. AMQP

33.2.1. RabbitMQ support
33.2.2. Sending a Message
33.2.3. Receiving a Message

33.3. Apache Kafka Support

33.3.1. Sending a Message
33.3.2. Receiving a Message
33.3.3. Kafka Streams
33.3.4. Additional Kafka Properties

34. Calling REST Services with RestTemplate

34.1. RestTemplate Customization

35. Calling REST Services with WebClient

35.1. WebClient Runtime

35.2. WebClient Customization

36. Validation

37. Sending Email

38. Distributed Transactions with JTA

38.1. Using an Atomikos Transaction Manager

38.2. Using a Bitronix Transaction Manager

38.3. Using a Java EE Managed Transaction Manager

38.4. Mixing XA and Non-XA JMS Connections

38.5. Supporting an Alternative Embedded Transaction Manager

39. Hazelcast

40. Quartz Scheduler

41. Task Execution and Scheduling

42. Spring Integration

43. Spring Session

44. Monitoring and Management over JMX

45. Testing

45.1. Test Scope Dependencies

45.2. Testing Spring Applications

45.3. Testing Spring Boot Applications

45.3.1. Detecting Web Application Type
45.3.2. Detecting Test Configuration
45.3.3. Excluding Test Configuration
45.3.4. Testing with a mock environment
45.3.5. Testing with a running server
45.3.6. Using JMX
45.3.7. Mocking and Spying Beans
45.3.8. Auto-configured Tests
45.3.9. Auto-configured JSON Tests
45.3.10. Auto-configured Spring MVC Tests
45.3.11. Auto-configured Spring WebFlux Tests
45.3.12. Auto-configured Data JPA Tests
45.3.13. Auto-configured JDBC Tests
45.3.14. Auto-configured Data JDBC Tests
45.3.15. Auto-configured jOOQ Tests
45.3.16. Auto-configured Data MongoDB Tests
45.3.17. Auto-configured Data Neo4j Tests
45.3.18. Auto-configured Data Redis Tests
45.3.19. Auto-configured Data LDAP Tests
45.3.20. Auto-configured REST Clients
45.3.21. Auto-configured Spring REST Docs Tests
Auto-configured Spring REST Docs Tests with Mock MVC
Auto-configured Spring REST Docs Tests with REST Assured
45.3.22. Additional Auto-configuration and Slicing
45.3.23. User Configuration and Slicing
45.3.24. Using Spock to Test Spring Boot Applications

45.4. Test Utilities

45.4.1. ConfigFileApplicationContextInitializer
45.4.2. TestPropertyValues
45.4.3. OutputCapture
45.4.4. TestRestTemplate

46. WebSockets

47. Web Services

48. Calling Web Services with WebServiceTemplate

49. Creating Your Own Auto-configuration

49.1. Understanding Auto-configured Beans

49.2. Locating Auto-configuration Candidates

49.3. Condition Annotations

49.3.1. Class Conditions
49.3.2. Bean Conditions
49.3.3. Property Conditions
49.3.4. Resource Conditions
49.3.5. Web Application Conditions
49.3.6. SpEL Expression Conditions

49.4. Testing your Auto-configuration

49.4.1. Simulating a Web Context
49.4.2. Overriding the Classpath

49.5. Creating Your Own Starter

49.5.1. Naming
49.5.2. autoconfigure Module
49.5.3. Starter Module

50. Kotlin support

50.1. Requirements

50.2. Null-safety

50.3. Kotlin API

50.3.1. runApplication
50.3.2. Extensions

50.4. Dependency management

50.5. @ConfigurationProperties

50.6. Testing

50.7. Resources

50.7.1. Further reading
50.7.2. Examples

51. What to Read Next

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

推荐阅读更多精彩内容