随着官方文档学 SpringBoot 三:spring boot 特性

跟着官方文档学 SpringBoot 三:spring boot 特性

 

spring boot 特性

【启动相关】

 

1. 启动应用

在启动应用类的 main 方法中调用 SpringApplication 的 run():

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

 

2. 启动失败

如果应用启动失败了,FailureAnalyzers 会获取错误信息,并得到一个具体解决该问题的办法。

比如,8080 端口被占用,你可能会看到如下信息:

***************************
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.

 如何创建自己的失败分析器?

1)创建一个自定义的失败分析类,继承 AbstractFailureAnalyzer。如果自己无法处理异常问题,可以通过返回 null 来让其他实现类来处理;

2)在 META/spring.factories 文件中注册该类,如下:

org.springframework.boot.diagnostics.FailureAnalyzer=
com.example.ProjectConstraintViolationFailureAnalyzer

 如果需要访问 BeanFactory 或者 Environment,那么自定义的 FailureAnalyzer 可以通过实现 BeanFactoryAware 或者 EnvironmentAware 接口。

 

3. 定制 Banner

Banner,即应用启动时的图像,默认为 spring boot:

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

 通过在 classpath(比如在 resources 文件夹下) 添加一个 banner.txt 或者 在配置文件中设置 spring.banner.location 属性指定文件位置。如果文件的编码不是 UTF-8,那么需要设置 spring.banner.charset 指定编码。

 

除了添加文本文件,也可以添加 banner.gif,banner.jpg 或者 banner.png 的图片,或者设置 spring.banner.image.location 属性指定文件位置,图片会被转换为 ASCII 码的表现形式打印。

博主在 resources 下 添加了 banner.png :

 

启动应用后,显示效果:

 

 

延伸:什么是 classpath?

虽然,classpath 这个词翻译为中文,大家都知道,叫做“类路径”,那么究竟什么是类路径呢?想必许多同学和我一样曾经被困扰过。

classpath 指的是程序编译后 classes(类的字节码文件)所在的目录。在程序没有编译前,通常 .java 文件放置于 java 文件夹下(比如 src/main/java/service/UserService.java),而配置文件通常位于 resources 文件夹下(比如 src/main/resources/application.properties)。当程序编译后,java 文件夹下的 .java 文件会变为 target/classes 文夹下的.class 文件,resources 文件夹下的文件也会被置于 classes 文件夹下,此时的 classes 文件夹即项目的 classpath。

 

如果将 banner.txt 或者 banner.png 直接放置于 src/main/java 文件夹下,由于编译后不在 target/classes 文件夹下,因而是不会生效的。(IntelliJ IDEA 工具编译后的文件都在 target 目录下)

 

4. 定制 SpringApplication

当你觉得默认的 SpringApplication 无法满足使用时,可以通过创建一个本地(相对于 spring-boot-starter 而言)的实例来定制它。

比如,上一小节的定制 Banner 同样可以通过编程的方式实现:

public static void main(String[] args) {
        SpringApplication application = new SpringApplication(Example.class);
        application.setBanner(new MyBanner());
        application.run(args);
}

 也可以关闭它:

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

 

5. 流式 API

通过 SpringApplicationBuilder 对象可以实现链式调用多个方法,并且创建一个分层次的应用上下文环境:

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

 在创建分层的 应用上下文时需要注意一些限制,比如 Web 组件必须包含在子上下文中,并且子上下文和父上下文必须使用同一个 Environment。

 

6. 事件和监听器

一些事件是在 ApplicationContext 创建之前被触发的,因此无法在这些事件上注册 listener 作为一个 @Bean。可以通过 SpringApplication.addListeners() 或者 SpringApplicationBuilder.listeners() 方法进行注册;如果想要这些 listeners 自动注册,而不关心应用的创建方式,可以添加 META-INF/spring.factories 文件,通过如下示例方式注册 listener:

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

 

7. Web 环境

SpringApplication 会尝试创建正确类型的 ApplicationContext,默认情况下使用 AnnotationConfigApplicationContext 或者 AnnotationConfigServletWebServerApplicationContext,

这基于正在开发的是否为一个 web 应用。判断一个环境是否为 web 环境的算法是相当简单的,基于几个类是否存在而已。如果想要覆盖它判断环境类型的方式,

可以通过 setWebEnvironment(boolean webEnvironment)设置。通过调用 setApplicationContextClass() 方法可以自己控制 ApplicationContext。在 JUnit 测试环境下使用 SpringApplication时,推荐调用 setWebEnvironment(false) 方法。

 

8. 访问应用参数

如果需要访问那些被传递进 SpringApplication.run() 方法的参数,可以通过注入一个 org.springframework.boot.ApplicationArguments bean。ApplicationArguments 接口提供了访问原生的 String[] 参数和被解析为 option 和 non-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 还向 Spring 环境注册了一个 CommandLinePropertySource。这使得可以通过使用 @Value 注释来注入单个应用程序参数。

 

 

9 使用 ApplicationRunner 或者 CommandLineRunner

如果有一些特殊的代码需要在 SpringApplication 启动完成之前运行,那么可以通过实现 ApplicationRunner 或者 CommandLineRunner 接口来完成。这两个接口运行的方式都是一样的,

并且提供单一的 run 方法,run 方法会在 SpringApplication.run() 完成之前被调用。

 

CommandLineRunner 接口将应用参数当作简单的 string 数组进行访问,如下示例:

 

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

@Component
public class MyBean implements CommandLineRunner {

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

}
 如果定义了多个 CommandLineRunner 或者 ApplicationRunner beans,那么它们必须根据特定的顺序被调用。

 

可以通过额外实现 org.springframework.core.Ordered 接口,或者使用 org.springframework.core.annotation.Order 注解完成顺序调用。

 

10. 退出应用

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

 

此外,如果希望在 SpringApplication.exit() 被调用前执行一段特定的退出代码(exit code),那么这些 beans 可以通过实现 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 接口和实现 getExitCode()方法,在异常发生时,可以返回退出代码。

 

11. 管理员特性

通过指定 spring.application.admin.enabled 属性为 true(false)启用(不启用)管理员相关特性,这会将 SpringApplicationAdminMXBean 暴露在 MBeanServer 平台。可以通过使用这个特性远程管理 spring boot 应用,这对于服务包装实现很有用。

 

 

【外部化配置】

 

springboot 可以外部化配置,使得相同的代码可以适应不同的环境。简单讲,就是从外部的配置文件获取指定值,不写死代码。

使用 properties 文件、YAML 文件、环境变量、命令行参数这几种方式进行配置。这些配置的属性值可以通过 @Value 注解直接注入到 bean 中,可以通过 Spring 的 Environment 接口实现类来访问,

或者通过 @ConfigurationProperties 绑定到结构化对象。

 

为了实现属性值的合理的覆盖(因为配置方式有多种,可能存在键重复值覆盖),springboot 使用了一种特殊的顺序。属性将按以下顺序读取:

1. Devtools 全局设置。(当开启了 devtools 的情况下,在 ~/ .spring-boot-devtools.properties 配置)

2. 测试类中 @TestPropertySource 注解中的属性值

3. 测试类中 @SpringBootTest#properties 注解中的属性值

4. 命令行参数

5. SPRING_APPLICATION_JSON 的属性值(内嵌在环境变量或系统属性中的JSON)

6. ServletConfig 初始化参数

7. ServletContext 初始化参数

8. 来自 java:comp/env 的 JNDI 属性

9. Java 系统属性值(通过 System.getProperties() 获取的值)

10. 操作系统环境变量

11. RandomValuePropertySource 中有属性值的,并且符合 random.* 格式

12. 没被打进 jar 包的 profile-specific 应用属性(application-{profile}.properties 和 YAML 变量)

13. 被打进 jar 包的应用属性(application-{profile}.properties 和 YAML 变量)

14. 没被打进 jar 包的应用属性(application-{profile}.properties 和 YAML 变量)

15. 被打进 jar 包的应用属性(application-{profile}.properties 和 YAML 变量)

16. 在 @configuration 类上的 @PropertySource 注解属性

17. 默认属性(通过设置 SpringApplication.setDefaultProperties 指定的)

 

在举具体的示例之前,推荐先写一个使用了 name 属性值的组件(@Component),如下:

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

@Component
public class MyBean {

    @Value("${name}")
    private String name;

    // ...

}

 在 classpath (比如 resources 资源文件夹)中添加一个“application.properties”属性文件,在文件中指定 name 的值,如 name=Lina。

 对于一次性测试,可以在命令行中进行指定,比如:

java -jar app.jar --name="Spring"
 

 

SPRING_APPLICATION_JSON 的属性值可以通过环境变量的方式应用于命令行:

1. 在 UNIX shell 中可以使用如下命令:

$ SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar
 该命令表示 在 spring 中 acme.name 对应的属性值为 test。

 

2. 在系统属性(System property)中将 JSON 作为 spring.application.json 使用:

 

$ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar
        3. 通过使用命令行参数来使用 JSON:
$ java -jar myapp.jar --spring.application.json='{"name":"test"}'
        4. 将 JSON 作为一个 JNDI 变量:
java:comp/env/spring.application.json
        
1. 配置随机属性值
对于注入随机值来说, RandomValuePropertySource 很有用。它能产生 integers, longs, uuids, 或者 strings:
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}
 
2. 访问命令行属性值
默认情况下,SpringApplication 会将所有的命令行参数(以 -- 开头,如 --server.port=9000)转换为一个 property,并添加到 spring 环境中。
注意一下,命令行的属性值相对来说优先级是比较高的(排第四)。如果不想要将命令行参数作为属性值放入 spring 中,可以如下设置来关闭:
SpringApplication.setAddCommandLineProperties(false)
 
3. Application Property 文件
SpringApplication 从如下位置中的 application.properties 文件加载属性:
1) 当前目录下的 /config 目录中(经测试,文档中的“当前目录”为项目根目录,即与 src 同级目录。汗...)
2) 当前目录中(项目根目录)
3) classpath 里的 /config 目录下
4)classpath 根目录下
加载的优先级即以上排列的顺序。
如果不想将属性文件命名为“application.properties”,可以通过指定一个叫做 spring.config.name 的环境属性,比如:
// 设置命令行参数的方式
$ java -jar myproject.jar --spring.config.name=myproject
如果不想属性文件必须在默认读取的位置,可以如下自定义位置(示例指定了两个位置):
$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
 注意:由于为了决定那个文件必须被加载, spring.config.name 和 spring.config.location 这两个属性将会被很早使用,因此它们必须在环境属性中定义,比如定义在操作系统环境变量中,系统属性中或者命令行参数中。
如果 spring.config.location 对应的文件是一个目录(directory),那么必须以“/”结尾,这样它会和 spring.config.name 组合起来形成具体文件路径。
4. Application 的特定属性
application-{profile}.properties(简称“特定属性文件”) 相对与 application.properties 文件,它是一个更具体的配置文件,比如 application-dao.properties 文件具体配置持久层数据,如数据源,数据库等。
读取 application-{profile}.properties 文件的位置与 application.properties 相同,不过特定属性文件中数据的优先级总是高于 application.properties 中定义的。如果多个(相同的)特定属性文件被指定,那么后加载的生效。
比如,spring.profiles.active 属性指定的文件在使用 SpringApplication API 配置的文件之后添加,因此 spring.profiles.active 属性指定的具有更高优先级。
5. 属性文件中的占位符
在 application.properties 文件中先定义的属性,可以供后定义的属性使用:
app.name=MyApp
app.description=${app.name} is a Spring Boot application
 
 后续的 spring boot 特性,请见“spring boot 特性之...”系列!
 
 
 
 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相关内容推荐