本文主要参考Externalized Configuration
为了能让应用在不同的环境下运行,Spring Boot允许自定义配置文件,如properties文件、yaml文件、系统环境变量参数、命令行参数。配置文件的覆盖优先级如下
- Devtools全局配置
- @TestPropertySource注解
- @SpringBootTest的properties属性
- 命令行参数
- SPRING_APPLICATION_JSON
- ServletConfig、ServletContext
- JNDI
- Java System properties (System.getProperties())
- 系统环境变量
- 随机数配置
- 自定义配置文件
- application.properties
- @PropertySource
- Spring Boot内部默认的配置参数
Devtools全局配置
Developer Tools提供了一些开发帮助工具,在build.gradle添加依赖后启用。
configurations {
developmentOnly
runtimeClasspath {
extendsFrom developmentOnly
}
testRuntimeClasspath {
extendsFrom developmentOnly
}
}
dependencies {
developmentOnly("org.springframework.boot:spring-boot-devtools")
}
Spring Boot会读取在计算机用户的home目录下的.spring-boot-devtools.properties
文件里的配置参数到该计算级的所有Spring Boot应用中作为顶层配置,如Linux环境下root用户下~/.spring-boot-devtools.properties
文件。开发过程中,可以将一些个人参数记录在这个配置文件中,例如ip地址,机器uuid,datasource参数等。在该配置文件中的定义的配置环境并不会影响到应用配置的读取,官方原话是:
Profiles activated in
.spring-boot-devtools.properties
will not affect the loading of profile-specific configuration files.
但要注意,该配置优先级最高,设置的时候需要做好记录否则会出现"原因不明的bug",不过应该很少人会用到这个功能。分析下源码,就是加了一个配置切面,并把其设置为顶层配置:
public class DevToolsHomePropertiesPostProcessor implements EnvironmentPostProcessor {
private static final String FILE_NAME = ".spring-boot-devtools.properties";
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
File home = getHomeFolder();//System.getProperty("user.home")获取用户目录
File propertyFile = (home != null ? new File(home, FILE_NAME) : null);
if (propertyFile != null && propertyFile.exists() && propertyFile.isFile()) {
FileSystemResource resource = new FileSystemResource(propertyFile);
Properties properties;
try {
properties = PropertiesLoaderUtils.loadProperties(resource);
//成功加载后作为顶层配置
environment.getPropertySources().addFirst(
new PropertiesPropertySource("devtools-local", properties));
}
catch (IOException ex) {
throw new IllegalStateException("Unable to load " + FILE_NAME, ex);
}
}
}
@TestPropertySource注解
在测试的时候,可能会使用另一套测试专用的配置,该套配置的优先级高于系统环境变量、java系统参数、程序内部参数,@TestPropertySource
注解就是用来指定这一类配置的。该注解一共有5个参数可以设置:
- inheritLocations: boolean
是否从父类继承locations,默认为true - inheritProperties: boolean
是否从父类继承properties - locations\value: String[]
配置文件的路径,路径格式支持类路径("classpath:/com/spring/test/DemoTest.properties"),或者文件路径("file:/dir/DemoTest.properties"),路径名支持SpEL但不支持通配符。配置文件格式支持xml和properties,不支持yaml。 - properties: String[]
参数键值对支持3种语法"key=value", "key:value","key value",优先级高于从locations读取的参数。
如果使用注解的时候没有任何参数,那么会从标注了注解的测试类的包中尝试读取配置文件,例如测试类com.spring.test.DemoTest
,那么相应的默认配置文件为com.spring.test.DemoTest.properties
,如果没有找到默认的配置文件则抛出非法状态异常。
AbstractTestContextBootstrapper
在初始化上下文的时候会调用一个读取、合并配置的方法buildMergedContextConfiguration
,该方法通过工具类TestPropertySourceUtils
读取类的注解信息。TestPropertySourceUtils
从类的注解解析配置信息后返回一个可合并的配置源。
static MergedTestPropertySources buildMergedTestPropertySources(Class<?> testClass) {
Class<TestPropertySource> annotationType = TestPropertySource.class;
AnnotationDescriptor<TestPropertySource> descriptor = findAnnotationDescriptor(testClass, annotationType);
if (descriptor == null) {
return new MergedTestPropertySources();
}
List<TestPropertySourceAttributes> attributesList = resolveTestPropertySourceAttributes(testClass);
String[] locations = mergeLocations(attributesList);
String[] properties = mergeProperties(attributesList);
return new MergedTestPropertySources(locations, properties);
}
@SpringBootTest的properties属性
@SpringBootTest
的value\properties属性用于注入一些自定义的注解,语法要求和@TestPropertySource
的properties一样,这里就不详细展开了。
命令行参数
用命令行方式启动Spring Boot应用程序的时候,可以注入一些配置参数,参数的格式是--key=name
。举个简单的例子,程序直接输出一个参数,然后打成jar包后运行。
@SpringBootApplication
@Slf4j
public class App implements CommandLineRunner {
@Value("${cl.name:undefined}")
private String commandLineName;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
@Override
public void run(String... args) throws Exception {
log.info("Command line arguments: {}", Arrays.toString(args));
log.info("commandLineName: {}", commandLineName);
}
}
运行:
java -jar .\springbootconfiguraiton.jar --cl.name="Spring Boot Arguments"
从输出的结果中可以看到可以读取到命令行中的配置。
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.2.RELEASE)
2019-05-25 17:41:36.763 INFO 14416 --- [ main] demo.App : Starting App on DESKTOP-HTJRA28 with PID 14416 (G:\spring boot configuration\build\libs\springbootconfiguraiton.jar started by 66439 in G:\spring boot configuration\build\libs)
2019-05-25 17:41:36.769 INFO 14416 --- [ main] demo.App : No active profile set, falling back to default profiles: default
2019-05-25 17:41:36.859 INFO 14416 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@555590: startup date [Sat May 25 17:41:36 CST 2019]; root of context hierarchy
2019-05-25 17:41:39.662 INFO 14416 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2019-05-25 17:41:39.705 INFO 14416 --- [ main] demo.App : Started App in 3.534 seconds (JVM running for 4.473)
2019-05-25 17:41:39.708 INFO 14416 --- [ main] demo.App : Command line arguments: [--cl.name=Spring Boot Arguments]
2019-05-25 17:41:39.709 INFO 14416 --- [ main] demo.App : commandLineName: Spring Boot Arguments
2019-05-25 17:41:39.714 INFO 14416 --- [ Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@555590: startup date [Sat May 25 17:41:36 CST 2019]; root of context hierarchy
2019-05-25 17:41:39.730 INFO 14416 --- [ Thread-2] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
SPRING_APPLICATION_JSON
可以在环境变量中定义一个key为SPRING_APPLICATION_JSON的参数,值为json字符串,Spring Boot会解析该json字符串作为参数注入到系统中。SPRING_APPLICATION_JSON可以定义在环境变量、系统配置中,命令行也是可以的,例如命令行参数中用到的demo,执行以下的命令也应该能得到相同的参数结果。
java -jar .\springbootconfiguraiton.jar SPRING_APPLICATION_JSON='{"cl":{"name"="Spring Boot Arguments"}}'
结果输出是undefined,不知道原因,这个配置方式用的应该也很少,放弃研究。。。
ServletConfig、ServletContext
优先级是ServletConfig
> ServletContext
,可以在application.yml中设置:
server:
servlet:
context-parameters:
scp: 'scpV'
随机数配置
随机数配置大多用于测试,支持的类型如下:
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]}
其中long\int可以限制数据范围,[]是闭区间,()是开区间。
自定义配置文件
这个应该是我们用的最多的。首先说优先级,文件可以放在以下4个位置,相同文件从上到下覆盖。外部指的是启动应用程序的目录,例如gradle用application插件打包后,运行的脚本目录就是./
:
-
file:./config
,在外部的config
目录下 -
file:./
,在外部的目录下 -
classpath:/config/
,resources/config
下 -
classpath:/
,resources
下
文件的命名为application-[当前激活的环境名].[yml/properties]
,当前激活的配置可以用spring.profile.active=[当前激活的环境名]
定义,多个环境名用逗号分隔,未设置时用default
标识。关于如果修改默认的加载路径和文件名,后面会继续讨论。
application.properties
Spring Boot系统启动时默认会读取的配置文件,支持properties\yml格式。也就是说,会先加载application.properties
,根据spring.profile.active
的设置加载相应的application-XX.properties
配置,然后按优先级合并配置文件。
不同文件目录下application.properties的优先级和自定义配置文件的顺序是一样的。
@PropertySource
类似@TestPropertySource注解,在项目中可以方便的注入自定义的配置文件,注解一共有5个参数:
- value: String[]
配置文件的路径,路径格式支持类路径("classpath:/com/spring/test/DemoTest.properties"),或者文件路径("file:/dir/DemoTest.properties"),路径名支持SpEL但不支持通配符。配置文件格式支持xml和properties,不支持yaml。 - encoding: String
字符集,默认为空,使用系统的字符集。 - ignoreResourceNotFound: boolean
当找不到资源文件时,是否忽略,默认为false,找不到资源的时候会报错。 - name: String
每一套PropertySource都有一个唯一的资源限定名,可以通过propertySource.getName()
获取,默认为空,Spring根据资源类型(ClassPathResouce\FileSystemResource)生成一个资源名。 - factory: subClass of PropertySourceFactory
解析资源的工厂类,一般用默认的即可。