第4章 零XML配置的Spring Boot Application
Spring Boot 提供了一种统一的方式来管理应用的配置,允许开发人员使用属性properties文件、YAML 文件、环境变量和命令行参数来定义优先级不同的配置值。零XML配置的Spring Boot Application,是基于Spring JavaConfig。
application.properties配置文件使用
我们在上一章中,默认的服务端口是8080,如果我们想自定义端口号,在SpringBoot中怎么搞?
OK,很简单。SpringBootApplication有个系统级的配置文件application.properties
我们只需要在里面加一行配置即可:
server.port=5678
启动应用,你讲看到:
2017-04-04 23:31:21.673 INFO 90250 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2017-04-04 23:31:21.774 INFO 90250 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 5678 (http)
2017-04-04 23:31:21.780 INFO 90250 --- [ main] com.example.DemoApplication : Started DemoApplication in 4.712 seconds (JVM running for 5.511)
TomcatEmbeddedServletContainer已经在5678端口监听请求了。
浏览器访问:http://localhost:5678/hello
你会看到成功输出:
Hello World! Tue Apr 04 23:32:38 CST 2017
application.properties是Spring Boot约定的配置文件。Spring Boot基于这个配置文件的配置机制充分体现了Spring Boot遵循的“约定优于配置”的原则。
Spring Boot几乎所有的配置项都可以在这个文件中配置,如果不配置,则使用默认项。Spring Boot会检测配置项的key,启动相应的自动配置模块。
下面具体介绍一些在application.properties配置中的特性和使用方法。
1.数据源datasource配置
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
当Spring Boot一旦检测到数据库配置相关的配置项(spring.datasoure.*),Spring Boot会自动根据相应的配置项,建立数据库连接池。
这个application.properties里面到底有哪些key是可配置的呢?在SpringBoot官网文档给出了详尽的配置以及说明。在Appendix A. Common application properties中: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#common-application-properties
不完全统计,这个完整的application properties文件,有1183行。其中,属性key有950个。当你看到如此庞大的配置,你一定会被吓到。不过,在实际项目中,我们如果遵循Spring Boot的约定,通常不需要我们单独指定太多配置。
虽然是零XML配置,但是“有些配置的事情”,还是必须要做的。这里SpringBoot的实现方案,充分体现了Spring框架的“约定优于配置”理念。通过约定俗成的规范,很多问题的解决讲得到大大的简化。
2.在application.properties自定义属性与加载
我们可以将自己自定义的属性配置在application.properties中(注意不要和Spring Boot的默认配置的key重复),然后在java类中通过@Value("${属性名}")注解来加载对应的配置属性,例如:application.properties文件中有如下自定义配置项:
com.easy.springboot.h5perf.app.name = H5性能测试平台
则在java中有如下应用:
@Component
public class TestProperties {
@Value("${com.easy.springboot.h5perf.app.name }")
private String appName;
}
3.配置项值的引用
在application.properties中的各个参数之间可以直接通过“${keyname}”引用来使用。
在application.properties中也可以通过“@keyname@”来引用pom文件中的元素节点值,例如:
pom文件中有如下定义:
<properties>
<project.name >easy-springboot-demo </ project.name >
<project.version>1.0</ project.version >
</properties>
则在application.properties文件中可以通过如下方式引用:
# 项目名称
com.easy.springboot.project.name = @project.name@
# 项目版本
com.easy.springboot.project.version = @project.version@
4.随机数属性value
在一些情况下,有些参数我们需要希望它不是一个固定的值,Spring Boot的属性配置文件中可以通过${random}来产生int值、long值或者string字符串,来支持属性的随机值。
例如:
# 随机字符串
com.easy.springboot.string=${random.value}
# 随机int
com.easy.springboot.number=${random.int}
# 随机long
com.easy.springboot.bignumber=${random.long}
# 100以内的随机数
com.easy.springboot.range1=${random.int(100)}
# 100-200的随机数
com.easy.springboot.range2=${random.int[100,200]}
属性配置文件的位置
spring会从classpath下的/config目录或者classpath的根目录查找application.properties或application.yml。
/config优先于classpath根目录。
Spring Boot 配置的管理
一般在一个项目中,总是会有好多个环境。比如:
开发环境 -> 测试环境 -> 预发布环境 -> 生产环境
在应用中管理配置是一个重要的问题,尤其是在应用需要部署到多个环境中时。通常会需要为每个环境提供一个对应的属性文件,用来配置各自的数据库连接信息、服务器信息和第三方服务账号等。通常的应用部署会包含开发、测试和生产等若干个环境。
常规情况下,我们都知道Spring Boot的配置会从application.properties中读取。实际上,从resource目录下的application.properties文件读取是Spring Boot配置链中的一环而已。
配置的优先级
Spring Boot提供了一种优先级配置读取的机制来帮助我们从这种困境中走出来。
Spring Boot 所提供的配置优先级顺序比较复杂。按照优先级从高到低的顺序,具体的列表(从高到低)如下所示。
1.命令行参数(优先级最高)。
2.通过 System.getProperties() 获取的 Java 系统参数。
3.操作系统环境变量。
4.从 java:comp/env 得到的 JNDI 属性。
5.通过 RandomValuePropertySource 生成的random.*属性。
6.jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件,通过spring.config.location参数指定
7.jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件
8.jar包外部的application.properties或application.yml(不带spring.profile)配置文件
9.jar包内部的application.properties或application.yml(不带spring.profile)配置文件
10.应用 Java配置类,包含@Configuration注解的 Java 类,通过@PropertySource注解声明的属性文件。
11.通过SpringApplication.setDefaultProperties声明的默认属性。
如果Spring Boot在优先级更高的位置找到了配置,那么它就会忽略优先级低的配置。下面我们简单讲讲这些优先级。
命令行参数配置
Spring Boot 的这个配置优先级看似复杂,其实是很合理的。命令行参数的优先级之所以被设置为最高,是因为可以方便我们在测试或生产环境中快速地修改配置参数值,而不需要重新打包和部署应用。
SpringApplication 类默认会把以“--”开头的命令行参数转化成应用中可以使用的配置参数,如 “--name=Alex” 会设置配置参数 “name” 的值为 “Alex”。
如果不需要这个功能,可以通过
SpringApplication.setAddCommandLineProperties(false)
禁用解析命令行参数。
random.*属性
RandomValuePropertySource 可以用来生成测试所需要的各种不同类型的随机值,从而免去了在代码中生成的麻烦。RandomValuePropertySource 可以生成数字和字符串。数字的类型包含 int 和 long,可以限定数字的大小范围。以“random.”作为前缀的配置属性名称由 RandomValuePropertySource 来生成,代码示例:
user.id=${random.value}
user.count=${random.int}
user.max=${random.long}
user.number=${random.int(100)}
user.range=${random.int[100, 1000]}
属性文件配置
属性文件是最常见的管理配置属性的方式。Spring Boot 提供的 SpringApplication 类会搜索并加载 application.properties 文件来获取配置属性值。SpringApplication 类会在下面位置搜索该文件:
1.当前目录的/config子目录
2.当前目录
3.classpath 中的/config包
4.classpath
上面的顺序也表示了该位置上包含的属性文件的优先级。优先级按照从高到低的顺序排列。
可以通过spring.config.name这个key的配置属性来指定不同的属性文件名称。也可以通过spring.config.location来添加额外的属性文件的搜索路径。
对于配置属性,可以在代码中通过“@Value”来使用,例如
@RestController
@EnableAutoConfiguration
public class Application {
@Value("${name}")
private String name;
@RequestMapping("/")
String home() {
return String.format("Hello %s!", name);
}
}
其中,@Value("${name}")注解的意思就是,变量 name 的值来自配置属性中的name属性。
YAML格式的配置文件
相对于属性文件来说,YAML 是一个更好的配置文件格式。
当有前缀的情况下,使用.yml格式的配置文件更简单。
注意:使用.yml时,属性名的值和冒号中间必须有空格,如name: SpringBoot正确,SpringBoot就是错的。
YAML 在 Ruby on Rails 中得到了很好的应用。YAML是JSON的一个超集,也是一种方便的定义层次配置数据的格式。它的基本语法规则如下:
大小写敏感
使用缩进表示层级关系
缩进时不允许使用Tab键,只允许使用空格。
缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
# 表示注释,从这个字符一直到行尾,都会被解析器忽略。
只要你将SnakeYAML 库放到classpath下,SpringApplication就会自动支持YAML,以作为properties的替换。当然,我们通常不需要这么做,因为我们一般都会使用Starters,在spring-boot-starter里会自动加载SnakeYAML。
Spring框架提供两个便利的类用于加载YAML文档,YamlPropertiesFactoryBean会将YAML加载为Properties,YamlMapFactoryBean会将YAML加载为Map。
Spring Boot使用application.properties默认了很多配置。但需要自己添加一些配置的时候,我们应该怎么做呢。
例如,下面这段yml格式的配置
environments:
dev:
url: http://dev.easy.springboot.com
name: Easy Spring Boot
会转化为:
environments.url=http://dev.easy.springboot.com
environments.name=Easy Spring Boot
如果我们自定义了属性key,在代码中怎样使用上面的配置信息呢?利用Spring DataBinder工具集,Spring Boot通过注解@ConfigurationProperties 和@EnableConfigurationProperties 来完成这件事情。
在代码中对应的Bean对象:
@ConfigurationProperties(prefix="environments")
public class EnvironmentsConfig {
private String url;
private String name;
public String getUrl(){ return this.url; }
public void setUrl(String url){ this.url = url; }
public String getName(){ return this.name; }
public void setName(){ this.name = name; }
}
有些时候,我们还会配置类似下面这样的列表的属性
my:
servers:
- dev1.easy.com
- dev2.easy.com
会转化为:
my.servers[0]=dev1.easy.com
my.servers[1]=dev2.easy.com
使用@ConfigurationProperties这个注解来实现属性Bean的绑定,需要在Bean里面添加一个java.util.List(或者Set)类型的属性,然后写好setter(setter也可以换成初始化一个对象),getter:
@ConfigurationProperties(prefix="my")
public class ServersConfig {
private List<String> servers = new ArrayList<String>();//initialize it with a mutable value, equals a setter
public List<String> getServers() {
return this.servers;
}
}
因为YamlPropertySourceLoader类能够将YAML作为PropertySource导出到Spring Environment。所以我们可以使用常用的@Value注解配合占位符语法访问YAML属性。
另外,当我们使用@ConfigurationProperties注解定义配置的Bean时,需要在SpringBoot Application启动类上标注@EnableConfigurationProperties。
使用 Java配置类
Spring 框架本身提供了多种的方式来管理配置属性文件。Spring 3.1 之前可以使用 PropertyPlaceholderConfigurer。Spring 3.1 引入了新的环境(Environment)和概要信息(Profile)API,是一种更加灵活的处理不同环境和配置文件的方式。
Spring Profiles提供了一种隔离应用程序配置的方式,并让这些配置只在特定的环境下生效。任何@Component或@Configuration都能注解@Profile,从而限制加载它的环境:
@Configuration
@Profile("production")
public class ProductionConfiguration {
// ...
}
指定的属性方式常用的有2种:
1.配置在application.properties中:
spring.profiles.active=production
2.或使用命令行开关:
--spring.profiles.active=production
如果应用中包含多个 profile,可以为每个 profile 定义各自的属性文件,按照application-{profile}.properties(.yml)来命名。
使用CommandLineRunner
实际应用中,有时候我们在项目服务启动的时候就去加载一些数据或做一些事情这样的需求。 以前比较常见的做法是写再static代码块中。
在Spring Boot中,它提供了一个CommandLineRunner接口,实现这个接口的类总是会被优先启动,并优先执行CommandLineRunner接口中提供的run()方法。例如
public class ApplicationConfigure implements CommandLineRunner {
@Override
public void run(String... strings) throws Exception {
//TODO : 比如说,在这里预先加载的一些方法,类,属性等。
}
}
如果有多个CommandLineRunner接口实现类,那么可以通过注解@Order
来规定所有实现类的运行顺序。
小结
Spring Boot 它抛弃了Spring 中繁琐的xml配置文件的方式,声明式注解的方法为服务开发提供快速简洁的配置方式。在Spring Boot 中,我们会发现,我们其实不用做一些基本的配置也能直接运行刚创建好的工程项目,因为它内嵌了很多基本的通用的配置组件而不需要我们自己来做一些重复的配置工作。
Spring Boot让环境配置变得轻松很多。
参考资料:
1.https://www.ibm.com/developerworks/cn/java/j-lo-spring-boot/index.html
2.http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#common-application-properties
网友评论