SpringBoot advanced - third-party bean property binding
1. Basic binding
1. @ConfigurationProperties annotation
The @ConfigurationProperties annotation is used to bind the member properties in the bean to the properties in the configuration file
Can be annotated on a class or method
custom bean
Just annotate @Component and @ConfigurationProperties on the class
For example, the yaml configuration file has the following configuration:
test-bean:
ipaddress: 127.0.0.1
port: 8080
There are the following TestBean:
@Data
@Component
@ConfigurationProperties(prefix = "test-bean")
// @ConfigurationProperties("test-bean") // 默认即为 prefix 属性指定
public class TestBean {
private String ipAddress;
private int port;
}
The prefix attribute of the @ConfigurationProperties annotation is used to specify the properties in the binding configuration
You can get the bean after getting the applicationContext in the startup class to see the effect:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
TestBean bean = applicationContext.getBean(TestBean.class);
System.out.println(bean);
// 输出:TestBean(ipAddress=192.168.100.100, port=8088)
}
}
Solve the error
In idea, the custom bean annotation @ConfigurationProperties may have the following errors
Does not affect program operation
If you don’t want to see it, you can introduce the following dependencies to solve it
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
third-party beans
Write a method to return the bean in the configuration class, and annotate @Bean and @ConfigurationProperties on the method
Take DruidDataSource in Druid connection pool as an example
Pay attention to importing druid dependencies instead of druid-spring-boot-starter
The yaml configuration is as follows:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
We test the startup class as a configuration class:
@SpringBootApplication
public class Application {
@Bean
@ConfigurationProperties(prefix = "datasource")
public DruidDataSource druidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("noting");
return dataSource;
}
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
DruidDataSource dataSource = applicationContext.getBean(DruidDataSource.class);
System.out.println(dataSource.getDriverClassName());
// 输出:com.mysql.cj.jdbc.Driver
}
}
Note that the attribute set in the @Bean method will not affect the configuration in the configuration file
As in the above example, the setDriverClassName method is called in the method, but the output is still the attribute value in the yaml configuration
The value given by the set method in the @Bean method can be regarded as the default configuration, and the value in the yaml file is the final configuration
2. @EnableConfigurationProperties 注解
@EnableConfigurationProperties is annotated on the configuration class or startup class to specify the class that enables the binding, and multiple classes use an array
Its effect is the same as the annotation @Component on the bean, but both cannot take effect on one bean at the same time, and an error will be reported because two identical beans are found
Classes added by @EnableConfigurationProperties must be annotated with @ConfigurationProperties
@EnableConfigurationProperties will not be associated with @Bean annotated methods
The TestBean example is as follows:
@Data
//@Component
@ConfigurationProperties(prefix = "test-bean")
public class TestBean {
// ...
}
Startup class:
@SpringBootApplication
@EnableConfigurationProperties({
TestBean.class})
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
TestBean bean = applicationContext.getBean(TestBean.class);
System.out.println(bean);
}
}
2. Loose binding
Loose binding (or loose binding), which means that the binding property does not have to be strictly consistent with the property name
In the @ConfigurationProperties annotation, you can add any dash "-", but it cannot start with a dash and cannot contain uppercase letters. In fact, only lowercase letters and dashes can be used
In the yaml configuration file, any dash "-", underscore "_" and space can be added to the attribute name, which will not affect the matching of the binding
Example below:
TestBean:
@Component
@ConfigurationProperties(prefix = "test-bean") // 可添加任意的 '-',但不可符号开头,也不可含大写字母
@Data
public class TestBean {
private String ipAddress; // 变量名也可添加 '_'
}
yaml:
# 以下均可正常匹配
testbean:
ip-address: 192.168.0.101 # 烤肉串格式(官方推荐)
ip_address: 192.168.0.102 # 下划线格式
IP_ADDRESS: 192.168.0.103 # 常量格式,大写与下划线
"ip address": 192.168.0.104 # 空格或双引号也是可以的
-_iPa_d d-- Re ss: 192.168.0.105 # 乱七八糟,只要字母可匹配就能生效
The officially recommended format is the kebab format. If there are multiple matching attributes, only the kebab format will take effect
In addition, according to Mr. Dark Horse Li, when @Value injects attribute values, loose matching is not supported
But it is ok when I personally test, the test class is as follows:
@SpringBootTest
class ApplicationTests {
@Value("${mytest.message}")
private String message;
@Test
void contextLoads() {
System.out.println(message);
// 输出:Hello world!!
}
}
The yaml file is as follows:
my-test:
_MESSAGE-: Hello world!!
As long as there are no symbols other than dashes and lowercase letters in @Value
3. Commonly used units of measurement
Duration means a period of time, such as 3 minutes, 5 hours
DataSize indicates the file size, such as 10B, 5MB
Such properties can specify units when configuring
as follows:
@Component
@ConfigurationProperties(prefix = "test-bean")
@Data
public class TestBean {
// 指定默认时间单位,未设置则为毫秒 ms
@DurationUnit(ChronoUnit.SECONDS)
private Duration serverTimeout;
// 指定文件单位,未设置则为字节 B
@DataSizeUnit(DataUnit.KILOBYTES)
private DataSize dataSize;
}
yaml configuration file:
test-bean:
server-timeout: 3ms #只支持天以内的单位:ms, s, m, h, d
data-size: 10B #必须大写:B, KB, MB, GB, TB
4. Attribute verification
For example, the setting range of the port number is 0 ~ 65535, we hope to verify the configuration at startup
- Introduce two dependencies of validation-api and hibernate-validator
validation-api provides the interface, and hibernate-validator provides the implementation
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
- Annotate @Validated on the bean
- Annotation validation rules on attributes
Examples are as follows:
@Data
@Component
@Validated
@ConfigurationProperties(prefix = "test-bean")
public class TestBean {
@Max(value = 65535, message = "最大端口号为 65535")
@Min(value = 0, message = "最小端口号为 0")
private int port;
}
There are other rules that can be customized in the javax.validation.constraints package, you can study it yourself
5. Hexadecimal Conversion Rules
Configuring pure numbers in the yaml file may lead to unexpected results, as shown in the following example:
yaml configuration:
my-test:
password: 012345
Test class:
@SpringBootTest
class ApplicationTests {
@Value("${my-test.password}")
private String password;
@Test
void contextLoads() {
System.out.println(password);
// 输出:5349
}
}
It can be found that the value of password is configured as 012345, but it turns out to be 5349
This is because 012345 triggers the octal conversion format
0 and so the number is less than 8, it will be converted to decimal as octal, and then injected into the attribute
Also, 0b starts with binary, and 0x starts with hexadecimal
Such as 0b_0101_0101 and 0x3f3f3f3f
It is recommended that the attributes of the string type be enclosed in double quotes, especially for passwords that are difficult to troubleshoot.