springboot绑定配置属性

背景

想通过 BeanDefinitionRegistryPostProcessor 代码注册bean, bean注册基于 ConfigurationProperties 配置实现

实现

application.yaml

1
2
3
4
5
6
7
8
9
idea360:
name: cuishiying
blog: http://idea360.cn

cluster:
companyid:
g1: 1,2,3,4-10
g2: 10-100
g3: 100-200
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.example.demo.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
import org.springframework.boot.env.PropertiesPropertySourceLoader;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.context.annotation.Configuration;

/**
* {@link ConfigurationPropertiesBindingPostProcessor}
* {@link YamlPropertySourceLoader}
* {@link PropertiesPropertySourceLoader}
*
* @author cuishiying
* @date 2023-08-18
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "idea360")
public class BinderProperties {

private String name;

private String blog;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.example.demo.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

/**
* @author cuishiying
* @date 2023-08-18
*/
@Slf4j
@Component
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
log.info("exec custom postProcessBeanDefinitionRegistry");
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

}

@Override
public void setEnvironment(Environment environment) {
BindResult<BinderProperties> bindResult = Binder.get(environment).bind("idea360", BinderProperties.class);
BinderProperties binderProperties = bindResult.get();
log.info("binderProperties: {}", binderProperties);
}
}

日志输出

1
2
2023-08-18 14:46:55.979  INFO 15148 --- [           main] ustomBeanDefinitionRegistryPostProcessor : binderProperties: BinderProperties(name=cuishiying, blog=http://idea360.cn)
2023-08-18 14:46:55.979 INFO 15148 --- [ main] ustomBeanDefinitionRegistryPostProcessor : exec custom postProcessBeanDefinitionRegistry

Yaml基础使用

依赖

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.15.0</version>
</dependency>

java绑定对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package cn.idea360.yaml.yaml;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;

/**
* @author cuishiying
*/
@Data
@ConfigurationProperties(prefix = "cluster.companyid")
public class ClusterProperties {

private List<String> g1;
private List<String> g2;
private List<String> g3;

}

单元测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package cn.idea360.yaml;

import cn.idea360.yaml.yaml.ClusterProperties;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.util.ResourceUtils;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Properties;

@Slf4j
//@SpringBootTest
class YamlDemoApplicationTests {

@Test
void yaml() throws IOException {
String yamlFile = "application.yaml";
InputStream inputStream = YamlDemoApplicationTests.class.getClassLoader().getResourceAsStream(yamlFile);
String yaml = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());
System.out.println("原始yaml文本:\n" + yaml);

byte[] contentBytes = yaml.getBytes(StandardCharsets.UTF_8);
YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
yamlPropertiesFactoryBean.setResources(new Resource[]{new ByteArrayResource(contentBytes)});
Properties properties = yamlPropertiesFactoryBean.getObject();
System.out.println("YamlPropertiesFactoryBean:\n" + properties);

StandardEnvironment environment = new StandardEnvironment();
DefaultResourceLoader defaultResourceLoader = new DefaultResourceLoader();
Resource resource = defaultResourceLoader.getResource(yamlFile);
List<PropertySource<?>> propertySourceList = new YamlPropertySourceLoader().load("yaml", resource);
propertySourceList.forEach(propertySource -> environment.getPropertySources().addLast(propertySource));
ClusterProperties clusterProperties = Binder.get(environment).bindOrCreate("cluster.companyid", ClusterProperties.class);
System.out.println("Binder属性:\n" + clusterProperties);
}

@Test
void readYaml() throws Exception {
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("application.yaml");
String absolutePath = ResourceUtils.getFile("classpath:application.yaml").getAbsolutePath();
Yaml yaml = new Yaml();
Object load = yaml.load(inputStream);
ObjectMapper objectMapper = new ObjectMapper();
log.info("path: {}, content: {}", absolutePath, objectMapper.writeValueAsString(load));
}

@Test
void writeYaml() throws Exception {
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
options.setPrettyFlow(true);
Yaml yaml = new Yaml(options);

String json = "{\"idea360\":{\"name\":\"cuishiying\",\"blog\":\"http://idea360.cn\"}}";
ObjectMapper objectMapper = new ObjectMapper();
Object yamlContent = objectMapper.readValue(json, Object.class);
String dump = yaml.dump(yamlContent);
log.info("yaml:\n{}", dump);
}

}

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
原始yaml文本:
idea360:
name: cuishiying
blog: http://idea360.cn

cluster:
companyid:
g1: 1,2,3,4-10
g2: 10-100
g3: 100-200
YamlPropertiesFactoryBean:
{cluster.companyid.g1=1,2,3,4-10, idea360.name=cuishiying, cluster.companyid.g2=10-100, cluster.companyid.g3=100-200, idea360.blog=http://idea360.cn}
Binder属性:
ClusterProperties(g1=[1, 2, 3, 4-10], g2=[10-100], g3=[100-200])

yaml和json互转

1
2
3
4
5
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.13.5</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static String convertYamlToJson(String yaml) {
try {
ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
Object obj = yamlReader.readValue(yaml, Object.class);
ObjectMapper jsonWriter = new ObjectMapper();
return jsonWriter.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}

private static String convertJsonToYaml(String json) {
try {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(json);
return new YAMLMapper().writeValueAsString(jsonNode);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}