一、配置文件概述
1.1 为什么选择YAML?
YAML以数据为中心,比JSON、XML等更适合做配置文件。对比来看:
|------------|--------------------------------------------|-----|
| 格式 | 配置示例 | 可读性 |
| XML | <server><port>8080</port></server> | 较差 |
| Properties | server.port=8080 | 一般 |
| YAML | server: port: 8080 | 优秀 |
二、YAML语法详解
2.1 基本语法
k:(空格)v:表示一对键值对,空格必须有,以空格的缩进来控制层级关系,左对齐的数据属于同一层级
属性和值大小写敏感
server:
port: 8080
servlet:
context-path: /boot3
2.2 值的写法
字面量(数字、字符串、布尔)
name: zhangsan
age: 18
boss: true
字符串引号规则:
双引号"":不会转义特殊字符。name: "zhangsan \n lisi" 输出为换行效果
单引号'':会转义特殊字符。name: 'zhangsan \n lisi' 输出为 zhangsan \n lisi
对象、Map(键值对):
person:
lastName: zhangsan
age: 18
boss: false
数组(List、Set):
lists:
- a
- b
- c
行内写法:
lists: [a, b, c]
三、配置文件值注入
3.1 @ConfigurationProperties 批量注入
创建Person.java,使用@ConfigurationProperties将配置文件中的属性映射到JavaBean中:
# application.yml
person:
last-name: 张三
age: 18
boss: false
birth: 2017/12/15
maps: {k1: v1, k2: 14}
lists: [a, b, c]
dog:
name: 旺财
age: 2
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String, Object> maps;
private List<Object> lists;
private Dog dog;
// 加上getter/setter
}
3.2 @Value 单个注入
如果只是在某个业务逻辑中获取配置文件中的某一项值,使用@Value更加轻量:
@RestController
public class HelloController {
@Value("${person.last-name}")
private String name;
@Value("${server.port}")
private String port;
}
3.3 两种方式对比
|------------|--------------------------|--------|
| 特性 | @ConfigurationProperties | @Value |
| 功能 | 批量注入配置文件属性 | 单个指定注入 |
| 松散绑定 | 支持 | 不支持 |
| SpEL | 不支持 | 支持 |
| JSR303数据校验 | 支持 | 不支持 |
| 复杂类型封装 | 支持 | 不支持 |
使用建议:如果专门编写了一个JavaBean来和配置文件进行映射,使用@ConfigurationProperties;如果只是在业务逻辑中获取某项配置值,使用@Value。
3.4 数据校验
@ConfigurationProperties支持JSR303数据校验:
@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {
@Email // lastName必须是邮箱格式
private String lastName;
// ...
}
3.5 @PropertySource 加载指定配置文件
默认情况下,@ConfigurationProperties从全局配置文件(application.properties/yml)中获取值。使用@PropertySource可以加载指定的配置文件:
@PropertySource(value = {"classpath:person.properties"})
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
// ...
}
3.6 @ImportResource 与 @Bean
Spring Boot默认不识别我们自己编写的Spring配置文件(如applicationContext.xml)。使用@ImportResource可以让配置文件生效:
@ImportResource(locations = {"classpath:spring.xml"})
@SpringBootApplication
public class HelloWorldMainApplication {
// ...
}
但Spring Boot更推荐使用全注解方式:
@Configuration // 表明这是一个配置类,替代Spring配置文件
public class MyAppConfig {
@Bean // 将方法返回值添加到容器中,默认id为方法名
public HelloService helloService() {
return new HelloService();
}
}
四、Profile多环境配置
4.1 多Profile文件
主配置文件命名为application-{profile}.properties/yml,例如:
application-dev.properties ------ 开发环境
application-prod.properties ------ 生产环境
application-test.properties ------ 测试环境
默认使用application.properties的配置。
4.2 YAML多文档块
YAML支持在一个文件中使用---分隔多个文档块:
# 默认配置
server:
port: 8080
---
# dev环境
spring:
profiles: dev
server:
port: 8081
---
# prod环境
spring:
profiles: prod
server:
port: 8082
4.3 激活指定Profile
在配置文件中指定:
spring:
profiles:
active: dev
命令行参数:
java -jar app.jar --spring.profiles.active=dev
虚拟机参数:
java -Dspring.profiles.active=dev -jar app.jar
五、配置文件加载位置与顺序
5.1 默认加载位置
Spring Boot启动时会扫描以下4个位置的配置文件,优先级由高到低:
-
file:./config/ (项目根目录下的config文件夹)
-
file:./ (项目根目录)
-
classpath:/config/ (类路径下的config文件夹)
-
classpath:/ (类路径根目录)
高优先级的配置会覆盖低优先级的配置,且所有位置的配置会互补合并。
5.2 外部配置加载顺序
Spring Boot支持从多种来源加载配置,优先级从高到低如下:
命令行参数(如 --server.port=8087)
来自java:comp/env的JNDI属性
Java系统属性(System.getProperties())
操作系统环境变量
RandomValuePropertySource配置的random.*属性值
由jar包外向jar包内寻找:
jar包外部的application-{profile}.properties/yml(带profile)
jar包内部的application-{profile}.properties/yml(带profile)
jar包外部的application.properties/yml(不带profile)
jar包内部的application.properties/yml(不带profile)
@Configuration注解类上的@PropertySource
SpringApplication.setDefaultProperties()指定的默认属性
参考资料:Spring Boot官方文档 External Configuration
六、自动配置原理
6.1 从"配置地狱"到"开箱即用"
Spring框架自2003年诞生以来,凭借IoC和AOP迅速成为Java企业级开发的事实标准,但随着生态越来越庞大,"配置地狱"问题日益严重。Spring Boot的自动配置机制正是为解决这一问题而生。
6.2 自动配置的核心流程
第一步:启动主配置类
Spring Boot启动时加载主配置类(@SpringBootApplication标注的类),开启自动配置功能@EnableAutoConfiguration-。
第二步:加载自动配置类
@EnableAutoConfiguration通过@Import(AutoConfigurationImportSelector.class)导入组件。AutoConfigurationImportSelector的selectImports()方法会调用SpringFactoriesLoader.loadFactoryNames()。
SpringFactoriesLoader扫描所有JAR包类路径下的META-INF/spring.factories文件,将其中配置的EnableAutoConfiguration对应的值全部加载到容器中。
在spring.factories中,配置了上百个自动配置类(xxxAutoConfiguration):
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
注意:Spring Boot 3.0之后,自动配置的注册方式从spring.factories迁移到了AutoConfiguration.imports文件,加载更加高效。
第三步:条件过滤
每个自动配置类上都标注了@Conditional系列注解,只有满足条件时配置类才会生效:
@Configuration
@ConditionalOnWebApplication // 必须是Web环境
@ConditionalOnClass(CharacterEncodingFilter.class) // 类路径存在指定类
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
// ...
}
第四步:配置属性绑定
自动配置类从对应的xxxProperties类中获取配置属性,而xxxProperties类通过@ConfigurationProperties与配置文件绑定:
@ConfigurationProperties(prefix = "spring.http.encoding")
public class HttpEncodingProperties {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
// ...
}
6.3 @Conditional 系列注解
|---------------------------------|------------------|
| 注解 | 作用 |
| @ConditionalOnClass | 类路径存在指定类时生效 |
| @ConditionalOnMissingClass | 类路径不存在指定类时生效 |
| @ConditionalOnBean | 容器中存在指定Bean时生效 |
| @ConditionalOnMissingBean | 容器中不存在指定Bean时生效 |
| @ConditionalOnProperty | 系统中指定的属性有指定的值时生效 |
| @ConditionalOnWebApplication | 当前是Web环境时生效 |
| @ConditionalOnNotWebApplication | 当前不是Web环境时生效 |
6.4 查看自动配置报告
在配置文件中添加debug=true,控制台会打印自动配置报告,方便查看哪些自动配置类生效、哪些没有生效:
properties
debug=true
Positive matches:(自动配置类启用的)
DispatcherServletAutoConfiguration matched:
@ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet'
@ConditionalOnWebApplication (required) found StandardServletEnvironment
Negative matches:(没有启动的自动配置类)
ActiveMQAutoConfiguration:
Did not match:
@ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory'