目前springboot是大部分java开发人员的首要选择,在平时工作中我们可能会有部分特殊的配置或者是自研的框架、模块需要用到application.yml自定义配置,以往通过@value
注解来读取配置文件,现在介绍一种更优雅的方式来读取配置文件,采用@ConfigurationProperties
注解
@ConfigurationProperties注解
我们模拟一个场景,与第三方系统对接,我们常常需要配置对方地址、部分接口信息、加密密钥等参数,我们先来看看最简单的情况如下
@ConfigurationProperties(prefix = "external")
@Getter
@Setter
@ToString
public class ExternalProperties {
private String baseUrl;
private String privateKey;
}
复制代码
配置文件中只含有最基本的类型,prefix
表示配置的前缀,这样一个最简单的配置类就完成了,但是目前配置类并不会生效,因为需要把配置类交给spring容器进行管理才能完成配置的自动注入,注意:一定需要有getter、setter方法,常见的方式有两种:
-
@Component注解
通过
@Component
注解可以让配置类成为spring bean也就加入了Spring容器,这种方式比较简单,但是不能细粒度的控制,需要注意的是这种方式需要spring扫描到配置类,常用的方式是@ComponentScan
或@SpringBootApplication
注解配置扫描路径,springboot默认是从启动类开始逐级向下扫描,所以启动类在最外层无需做特殊配置,只有在需要做更精细化的扫描时需要特别注意@ConfigurationProperties(prefix = "external") @Getter @Setter @ToString @Component public class ExternalProperties { private String baseUrl; private String privateKey; } 复制代码
-
@EnableConfigurationProperties注解
Spring Boot 2.2之后,提供了
@EnableConfigurationProperties
注解,并将value
设置为{ExternalProperties.class}
如下:@SpringBootApplication @EnableConfigurationProperties({ExternalProperties.class}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 复制代码
目前看来这种方式与
@Component
无太大差异,但是@EnableConfigurationProperties
还可以标记在@Configuration
类上,配合Spring Boot的自动配置功能,我们就可以做到根据需要来配置文件,如下:@Configuration @EnableConfigurationProperties({ExternalProperties.class}) public class ExternalAutoConfiguration { } 复制代码
spring.factories文件配置如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.demo.config.ExternalAutoConfiguration 复制代码
这样只有当ExternalAutoConfiguration自动配置时,配置类才会生效,这种方式常常是自定义模块或者框架使用的小技巧,如果不是很了解springboot的自动配置,简单的解释就是spring会将spring.factories配置的类自动加载。
application.yml配置如下:
external:
base-url: 'http://127.0.0.1:8080'
private-key: 'custom'
复制代码
启动项目简单打印一下配置类可以得到:
ExternalProperties(baseUrl=http://127.0.0.1:8080, privateKey=custom)
复制代码
至此最简单的配置类就完成了,接下来介绍一些相关的知识,在工作中容易犯错并比较实用的技巧
Spring 宽松绑定规则 (relaxed binding)
可能有点读者已经注意到上面的配置base-url使用的是kebab-case格式,配置类使用的是camelCased格式,但是Spring依然能准确的对baseUrl赋值,这是因为Spring采用宽松绑定规则,以下的配置都可以生效:
external:
base-url: 'http://127.0.0.1:8080'
base_url: 'http://127.0.0.1:8080'
baseUrl: 'http://127.0.0.1:8080'
baseurl: 'http://127.0.0.1:8080'
BASE-URL: 'http://127.0.0.1:8080'
BASEURL: 'http://127.0.0.1:8080'
复制代码
yaml常用语法
#纯量
demo:
int: 1
string: 'demo'
boolea: true
float: 1.2
date: 2021-06-16
#数组或者list ['1','2','3']
demo:
list:
- '1'
- '2'
- '3'
#对象或map,下面配置对应{key1: '1', key2: '2'}
demo:
map:
key1: '1'
key2: '2'
#或者
demo:
map: {key1: '1', key2: '2'}
#复杂的数组对象 [{key1:'1',key2:'2'},{key1:'3',key2:'4'}]
demo:
- key1: '1'
key2: '2'
- key1: '3'
key2: '4'
#或者
demo:
- {key1: '1', key2: '2'}
- {key1: '3', key2: '4'}
复制代码
非法的配置项
现在我们加入一个boolean类型配置项enabled,并进行如下配置
@ConfigurationProperties(prefix = "external")
@Getter
@Setter
@ToString
public class ExternalProperties {
private boolean enabled;
private String baseUrl;
private String privateKey;
}
复制代码
external:
enabled: error
private-key: 'custom'
base-url: 'http://127.0.0.1:8080'
复制代码
启动项目我们会得到如下错误,意思就是第11行配置项external.enabled
的value
为'error'字符串,不能转换为boolean类型,所以在开发中需要注意配置类型,并能快速定位到错误位置分析错误原因
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under 'external.enabled' to boolean:
Property: external.enabled
Value: error
Origin: class path resource [application.yml] - 11:12
Reason: failed to convert java.lang.String to boolean
Action:
Update your application's configuration
复制代码
如果我们希望Spring不会因为错误的配置而启动失败我们可以用ignoreInvalidFields=true
来进行配置,但是通常不建议这样做,会导致有些错误不能及时发现造成生产事故
@ConfigurationProperties(prefix = "external", ignoreInvalidFields = true)
@Getter
@Setter
@ToString
public class ExternalProperties {
private boolean enabled;
private String baseUrl;
private String privateKey;
}
复制代码
内嵌配置
我们可以用Lists,Maps和Classes做内嵌配置,
对于Classes,Spring是建议放在一个配置类里,采用内部类的方式来进行配置,现在我们加入接口配置和每个接口失败时的重试次数,注意:内部类需要是static的,不然配置不生效,如下
@ConfigurationProperties(prefix = "external", ignoreInvalidFields = true)
@Getter
@Setter
@ToString
public class ExternalProperties {
private boolean enabled;
private String baseUrl;
private String privateKey;
private List<Api> apis;
@Getter
@Setter
@ToString
@Component
public static class Api{
private String url;
private int retry;
}
}
复制代码
external:
enabled: true
private-key: 'custom'
base-url: 'http://127.0.0.1:8080/'
apis:
- url: '/token'
retry: 3
- url: '/users'
retry: 2
复制代码
启动项目,打印配置类
ExternalProperties(enabled=true, baseUrl=http://127.0.0.1:8080/, privateKey=custom, apis=[ExternalProperties.Api(url=/token, retry=3), ExternalProperties.Api(url=/users, retry=2)])
复制代码
有时候我们希望创建一个单独的类来进行复杂的配置,也就是把Api类设计成单独的类,这种方式的差异在于使用spring-boot-configuration-processor
生成自动提示时我们就需要@NestedConfigurationProperty
注解标记属性,后面自动配置会讲到
自动提示
引入依赖,打开idea annotation processing
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
复制代码
rebuild项目会自动生成spring-configuration-metadata.json文件,此时在进行配置时就会自动提示了
前面我们讲到的Api配置类设计成单独的配置类,在rebuild时并不会生成相关的spring-configuration-metadata.json配置,此时我们需要加上@NestedConfigurationProperty
注解,但是List和Map除外,如下
@ConfigurationProperties(prefix = "external", ignoreInvalidFields = true)
@Getter
@Setter
@ToString
public class ExternalProperties {
private boolean enabled;
private String baseUrl;
private String privateKey;
private List<Api> apis;
@NestedConfigurationProperty
private Api api;
}
复制代码
至此,关于@ConfigurationProperties的介绍与使用就讲完了,有问题的小伙伴欢迎提问,有错误的地方发送邮件到yp.yang7@foxmail.com,谢谢
近期评论