💡 **注意:**在需要读取配置文件时,SpringBoot 推荐使用注解处理器。该处理器主要功能是:
在配置文件里写属性的时候会自动匹配相关的实体类的属性加以提示。
加上后写配置文件的时候会有提 示。添加方式为:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
读取配置文件的几种方式
@ConfigurationProperties 读取主配置文件
该注解可以写在类、方法上。有一点要注意:只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties
功能。
- 示例一:直接写在类上,读取主配置文件里的属性
Student
类
@Component
@Data
@ConfigurationProperties(prefix = "student")
public class Student {
private long id;
private String name;
private int age;
private boolean gender;
private Map<String, String> scoreMap;
private List<String> hobbies;
}
主配置文件 application.yml
。其实也可以使用 application.properties
,效果是一样的。
student:
id: 9527
name: ikangjia
age: 17
hobbies:
- pingpang
- basketball
- lol
- cf
scoreMap: {math: 149, English: 139}
gender: true
注意: 其中的 @Component
是有意义的,如果删掉会报错,如下图
由上图可以看出:@ConfigurationProperties
注解不能单独使用。正如提示中显示,它至少需要:
- 通过
@EnableConfigrationProperties
注册:暂时不知何用,故略; - 标记为 Spring 组件:简单粗暴上注解(
@Component
或其他类似效果的均可); - 通过
@ConfigurationPropertiesScan
扫描:可以放在主类上,需要设置参数指定类或包名。
三者任一种方式来使其生效。
- 示例二:将其写在公共方法上。当想要将属性绑定到第三方组件时,这种做法很有用
还是上文的application.yml
文件
配置类
@Configuration(proxyBeanMethods = false)
public class StudentConfig {
@Bean
@ConfigurationProperties(prefix = "student")
public Student getStudent() {
return new Student();
}
}
@Value 读取主配置文件
@Value
一般用于注入单个或几个属性的时候使用,它支持 SPEL 表达式,很强大。它默认只能读取主配置文件里的自定义属性。
@Value 注解一般写在类属性上,一个属性对应一个 @Value 注解,所以一般在单个或很少几个属性值注入的时候才会使用该注解。
- 使用示例:
主配置文件 application.yml
student:
id: 9527
name: ikangjia
@Data
@Component
public class Student2 {
@Value("${student.id}")
private long id;
@Value("${student.name}")
private String name;
}
@PropertySource 指定资源文件位置
@PropertySource
注解用于指定资源文件读取的位置,它不仅能读取 properties 文件,也能读取 xml文件,并且通过 YAML 解析器,配合自定义 PropertySourceFactory
实现解析 YAML 文件。
要注意:使用自定义配置文件时,在自定义配置文件里的属性名不要与主配置里的属性名重复,否则会被主属性里的值给覆盖。即在同名的情况下:主属性里的属性值优先级高。
@ProperSource + Environment 读取任意位置的自定义配置文件
@ProperSource 与 @Configuration 注解联合使用,可以很方便的为程序注入属性
@Configuration
@PropertySource("classpath:config/student.properties")
public class AppConfig {
@Autowired
Environment env;
@Bean
public Student student() {
Student student = new Student();
student.setName(env.getProperty("student.name"));
return student;
}
}
@Data
public class Student {
private long id;
private String name;
}
// config/student.yml
student.id=95281
student.name=ikangjia
@PropertySource + @ConfigurationProperties 读取自定义配置文件
属性文件 resource/config/student.properties
student.id=9528
student.name=ikangjia
student.age=17
student.gender=true
Student 类
@Component
@PropertySource(value = {"classpath:config/student.properties"}, encoding = "utf-8")
@Data
@ConfigurationProperties(prefix = "student")
public class Student3 {
private long id;
private String name;
private int age;
private boolean gender;
}
@PropertySource + @Value 读取自定义配置文件
属性文件 resource/config/student.properties
student.id=9528
student.name=ikangjia
Student 类
@Component
@PropertySource(value = {"classpath:config/student.properties"}, encoding = "utf-8")
@Data
public class Student3 {
@Value("${student.id}")
private long id;
@Value("${student.name}")
private String name;
}
要注意:使用自定义配置文件时,在自定义配置文件里的属性名不要与主配置里的属性名重复,否则会被主属性里的值给覆盖。即在同名的情况下:主属性里的属性值优先级高。
@ImportSource 读取 Spring 配置文件
Spring Boot 里面没有 Spring 的配置文件,我们自己编写的配置文件,也不能自动识别;@ImportSource
注解的作用就是导入 Spring 的配置文件,让 Spring 的配置文件生效,加载进来。
(不推荐)使用示例:@ImportResource 标注在一个配置类上
// 导入Spring的配置文件让其生效
@ImportResource(locations = {"classpath:beans.xml"})
public class XxxConfig {
}
编写 Spring 的配置文件 beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="cn.ikangjia.entity.Student"></bean>
</beans>
- (推荐)使用示例:全注解的方式为容器添加组件
注意:在 SpringBoot 中,其实完全没必要按照上述示例给容器添加组件。SpringBoot 中推荐使用全注解的方式,即:
- 配置类 @Configuration:表明这是个 Spring 配置文件;
- 方法上添加注解 @Bean :给容器添加组件。
/**
* @Configuration:指明当前类是一个配置类;就是来替代之前的Spring配置文件
* 在配置文件中用<bean><bean/>标签添加组件
*/
@Configuration
public class MyAppConfig {
//将方法的返回值添加到容器中;容器中这个组件默认的 id 就是方法名
@Bean
public HelloService helloService02(){
System.out.println("配置类 @Bean 给容器中添加组件了...");
return new HelloService();
}
}
一些使用技巧
松散绑定
Spring Boot 使用一些放松的规则将属性绑定到 bean,因此不需要在属性名和 bean 属性名之间进行精确匹配。
Spring Boot 使用一些放松的规则将 Environment 属性绑定到 @ConfigurationProperties
bean,因此不需要在 Environment 属性名和 bean 属性名之间完全匹配。有用的常见例子包括破折号分隔的环境属性(例如,上下文路径绑定到 contextPath
)和大写的环境属性(例如,PORT 绑定到端口)。
作为一个例子,考虑以下 @ConfigurationProperties
类:
@ConfigurationProperties(prefix = "my.main-project.person")
@Data
public class MyPersonProperties {
private String firstName;
}
在前面的代码中,可以使用以下属性名:
示例 | 注意 |
My.main-project. person.first-name | 烤串的情况下,这是推荐使用的 .properties 和 .yml 文件。 |
My.main-project. person.firstName | 标准驼峰式语法。 |
My.main-project. person.first _ name | 下划线表示法,这是在 .properties 和 .yml 文件中使用的一种替代格式。 |
My.mainproject person firstname | 大写格式,在使用系统环境变量时推荐使用这种格式。 |
SpringBoot 建议使用烤肉串的形式,即
My.main-project. person.first-name
。
参数校验
在 @ConfigurationProperties
标注的类是支持参数校验的。@Value
不支持。
当使用 Spring 的 @Validated
注释对 @ConfigurationProperties
类进行注释时,Spring Boot 都会尝试对它们进行验证。您可以直接在配置类上使用 JSR-303 javax.validationconstraint
注释。为此,请确保您的类路径上有兼容的 jsr-303 实现,然后向字段添加约束注释,如下:
@ConfigurationProperties("my.service")
@Validated
public class MyProperties {
@NotNull
private InetAddress remoteAddress;
@Valid
private final Security security = new Security();
// getters/setters...
public static class Security {
@NotEmpty
private String username;
// getters/setters...
}
}
配置文件占位符
# 随机数
# 可以生成整数、长、 uuids 或字符串
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]}
# 占位符获取之前配置的值,如果没有可以是用:指定默认值
app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}
属性转换
Spring Boot 试图在外部应用程序属性绑定到 @ConfigurationProperties
bean 时将其强制转换为正确的类型。如果需要自定义类型转换,可以提供 ConversionService bean (使用名为 ConversionService 的 bean)或自定义属性编辑器(通过 customerconfigurebean )或自定义转换器(使用注释为 @ConfigurationProperties
Binding 的 bean 定义)。
转换持续时间
Spring Boot 专门支持持续时间的表达。如果您暴露了一个 java.time
。属性,则应用程序属性中的下列格式可用:
- 常规的长表示(除非指定了
@DurationUnit
,否则使用毫秒作为默认单位) -
Java.time.Duration
使用的标准iso-8601
格式 - 一种更易读的格式,其中的值和单位是耦合的(10秒表示10秒)
考虑下面的例子:
@ConfigurationProperties("my")
public class MyProperties {
@DurationUnit(ChronoUnit.SECONDS)
private Duration sessionTimeout = Duration.ofSeconds(30);
private Duration readTimeout = Duration.ofMillis(1000);
// getters / setters...
}
要指定30秒的会话超时,30秒、 PT30S 和30秒都是等效的。读取超时时间为500毫秒,可以以下列任何形式指定: 500、 PT0.5 s 和500毫秒。
你也可以使用任何受支援的单位,包括:
- 毫微秒
- 只需要几微秒
- 毫秒代表毫秒
- 再来一次
- 分钟
- 好几个小时
- 连续几天
默认单位是毫秒,可以使用 @DurationUnit
重写,如上面的示例所示。
如果您喜欢使用构造函数绑定,可以公开相同的属性,如下面的例子所示:
@ConfigurationProperties("my")
@ConstructorBinding
public class MyProperties {
// fields...
public MyProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout, @DefaultValue("1000ms") Duration readTimeout) {
this.sessionTimeout = sessionTimeout;
this.readTimeout = readTimeout;
}
// getters...
}
转换周期
除了持续时间,Spring Boot 还可以与 java.time
一起工作。期间类型。以下格式可用于应用程序属性:
- 常规的
int
表示形式(使用天作为默认单位,除非指定了@PeriodUnit
) -
Java.time
使用的标准 iso-8601格式 - 一种更简单的格式,其中值和单位对是耦合的(1y3d 表示1年和3天)
下列单元以简单格式支持:
- 很多年了
- 几个月
- 已经好几周了
- 连续几天
转换数据大小
Spring framework 具有一个 DataSize 值类型,该类型以字节表示大小。如果公开 DataSize 属性,应用程序属性中的下列格式可用:
- 常规长表示形式(除非指定了
@DataSizeUnit
,否则使用字节作为默认单元) - 一种更易读的格式,其中的值和单位是耦合的(10mb 表示10mb)
考虑下面的例子:
@ConfigurationProperties("my")
public class MyProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize bufferSize = DataSize.ofMegabytes(2);
private DataSize sizeThreshold = DataSize.ofBytes(512);
// getters/setters...
}
若要指定 10mb 的缓冲区大小,10 和 10mb 是等效的。可以将256字节的大小阈值指定为256或256b。
你也可以使用任何受支援的单位,包括:
- B 表示字节
- KB 表示千字节
- MB 表示兆字节
- GB 的 g 字节
- TB 为 TB
默认单元是字节,可以使用@DataSizeUnit 重写,如上面的示例所示。
如果您喜欢使用构造函数绑定,可以公开相同的属性,如下面的例子所示:
@ConfigurationProperties("my")
@ConstructorBinding
public class MyProperties {
// fields...
public MyProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize, @DefaultValue("512B") DataSize sizeThreshold) {
this.bufferSize = bufferSize;
this.sizeThreshold = sizeThreshold;
}
// getters...
}
@PropertySource 高级用法
源码阅读
//只能作用在类上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {
/**
* 指定资源名称,如果为空,就根据基础资源的描述生成。
*/
String name() default "";
/**
* 指定资源路径。
* 可以是 classpath:/xxx/xxxx
* 也可以是 file:/xxx/xxx/xx
*/
String[] value();
/**
* 是否忽略资源不存在的情况,如果不忽略,当资源不存在时就报错。默认不忽略。
* 此属性时spring4.0以后出现的。
*/
boolean ignoreResourceNotFound() default false;
/**
* 指定资源文件的编码格式。如果不指定就使用文件默认的。
* 此注解是spring4.3以后出现的。
*/
String encoding() default "";
/**
* 指定资源工厂,如果不指定,就使用默认的资源工厂。
*/
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}
自定义解析 YAML 文件
PropertySourceFactory
的默认实现 DefaultPropertySourceFactory
是解析不了 yaml 文件的,如果要解析,就要自定义实现。
我们就不自己解析 Yaml,直接引用第三方 jar 包进行解析。
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.23</version>
</dependency>
代码
/**
* 自定义Yaml解析工厂
*/
public class YAMLPropertySourceFactory implements PropertySourceFactory {
@Override
public org.springframework.core.env.PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException {
//创建一个YAML解析工厂。
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
//设置资源。
factory.setResources(encodedResource.getResource());
//获取解析后的Properties对象
Properties properties = factory.getObject();
//返回。此时不能像默认工厂那样返回ResourcePropertySource对象 ,要返回他的父类PropertiesPropertySource对象。
return name != null ? new PropertiesPropertySource(name, properties) :
new PropertiesPropertySource(encodedResource.getResource().getFilename(),properties);
}
}
//配置类:
@Configuration
@ComponentScan(basePackages = "propertysourcedemo")
//使用自定义工厂。
@PropertySource(value = "classpath:dbconfig/datasource-config.yaml",factory = YAMLPropertySourceFactory.class)
public class SpringConfig {
//通过SPEL表达式注入属性
@Value("${druid.driverClassName}")
private String driverClassName;
@Value("${druid.url}")
private String url;
@Value("${druid.username}")
private String username;
@Value("${druid.password}")
private String password;
//注册Druid数据源连接池
@Bean
public DruidDataSource druidDataSource(){
System.out.println("driverClassName====> " + driverClassName);
System.out.println("url====> " + url);
System.out.println("username====> " + username);
System.out.println("password====> " + password);
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driverClassName);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
}
参考文档