配置文件@ConfigurationProperties读取List、Map参数
package org.springframework.boot.context.properties;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigurationProperties {
......
}
支持的类型
List
custom.config.config1.folders[0]=/rootcustom.config.config1.folders[1]=/home/user1
custom.config.config1.folders[2]=/home/user2
对应的Java实现
@ConfigurationProperties(prefix = "custom.config.config1")public class Config1Properties{
private List<String> folders;
...
}
Map
custom.config.config1.map.key1=value1custom.config.config1.map.key2=value2
custom.config.config1.map.key3=value3
custom.config.config1.map.key4=value4
custom.config.config1.map.key5=value5
对应的Java实现
@ConfigurationProperties(prefix = "custom.config.config1")public class Config1Properties{
private Map<String, String> map;
...
}
Object
custom.config.config1.server.host=host1custom.config.config1.server.port=22
custom.config.config1.server.username=username1
custom.config.config1.server.password=password1
对应的Java实现
@ConfigurationProperties(prefix = "custom.config.config1")public class Config1Properties{
private ServerProperties server;
...
public static class ServerProperties {
private String host;
private int port;
private String username;
private String password;
...
}
}
Object List
custom.config.config1.servers[0].host=host1custom.config.config1.servers[0].port=22
custom.config.config1.servers[0].username=username1
custom.config.config1.servers[0].password=password1
custom.config.config1.servers[1].host=host2
custom.config.config1.servers[1].port=22
custom.config.config1.servers[1].username=username2
custom.config.config1.servers[1].password=password2
对应的Java实现
@ConfigurationProperties(prefix = "custom.config.config1")public class Config1Properties{
private List<ServerProperties> servers;
...
public static class ServerProperties {
private String host;
private int port;
private String username;
private String password;
...
}
}
Map的使用案例
比如,我们同时需要连接多个OSS(阿里对象存储),那我们就可以利用ConfigurationProperties的方式来配置多个。而且可以通过Spring的加载动态的注入到容器中去。
配置中心的配置:
# OSS1配置oss.multi.clients.accout.accessKeyId=xxx
oss.multi.clients.accout.accessKeySecret=xxx
oss.multi.clients.accout.privateEndpoint=xxx
oss.multi.clients.accout.bucketName=bucket-b-test
# OSS2配置
oss.multi.enabled=true
oss.multi.clients.xdtrans.accessKeyId=xxx
oss.multi.clients.xdtrans.accessKeySecret=xxx
oss.multi.clients.xdtrans.privateEndpoint=xxx
oss.multi.clients.xdtrans.bucketName=bucket-a-test
对应的Java实现
@Data@EqualsAndHashCode(callSuper = false)
@ConfigurationProperties(prefix = OssConstants.MULTI_CONFIG_PREFIX)
public class MultiOssProperties {
private Map<String, OssProperties> clients;
@Data
public static class OssProperties {
private String accessKeyId;
private String accessKeySecret;
private String publicEndpoint;
private String privateEndpoint;
private String bucketName;
private String object;
}
动态的定义我们需要的BeanDefinition。
public class MultiOssScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware { private ApplicationContext applicationContext;
@Setter
private MultiOssProperties multiOssProperties;
@Override
public void setBeanName(String name) {
log.info("init bean {}", name);
}
@Override
public void afterPropertiesSet() throws Exception {
Objects.requireNonNull(this.multiOssProperties, "multiOssProperties不能为空");
Objects.requireNonNull(this.applicationContext, "applicationContext不能为空");
}
// 动态的定义Bean
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
String beanSuffixName = StringUtils.capitalize(OssConstants.BEAN_SUFFIX_NAME);
// productCodes实际与oss.multi.clients.xdtrans的xdtrans保持一致
multiOssProperties.getClients().forEach((productCode, ossProperties) -> {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(OssClient.class,
() -> OssClientUtils.buildOssClient(ossProperties))
.getRawBeanDefinition();
beanDefinition.setInitMethodName("init");
beanDefinition.setDestroyMethodName("shutDown");
beanDefinitionRegistry.registerBeanDefinition(productCode + beanSuffixName, beanDefinition);
});
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
通过binder来让配置与对应的Java代码产生关系:
@Configuration@EnableConfigurationProperties(MultiOssProperties.class)
@ConditionalOnProperty(prefix = OssConstants.MULTI_CONFIG_PREFIX, value = "enabled")
public class MultiOssAutoConfiguration {
/**
* 初始化多个 ossClient 自动配置
*
* @param environment 环境变量属性
* @return OssClient 自动扫描注册器
*/
@Bean
public MultiOssScannerConfigurer multiOssScannerConfigurer(Environment environment) {
Binder binder = Binder.get(environment);
MultiOssProperties properties = binder.bind(OssConstants.MULTI_CONFIG_PREFIX, MultiOssProperties.class).get();
MultiOssScannerConfigurer multiOssScannerConfigurer = new MultiOssScannerConfigurer();
multiOssScannerConfigurer.setMultiOssProperties(properties);
return multiOssScannerConfigurer;
}
}
如何使用?
@Getter@AllArgsConstructor
public enum OssTypeEnum {
// 注意一下这里的beanName,要跟上面的postProcessBeanDefinitionRegistry保持一致
XDtransOssClient("xdtransOssClient", "oss1"),
DianDianOssClient("ddacctOssClient", "oss2"),
;
private final String beanName;
private final String desc;
// 根据BeanName来Spring容器中获取即可
public OssClient getBean() {
return SpringContextHolder.getBean(beanName, OssClient.class);
}
Binder是如何映射的?
通过上面的代码binder.bind(OssConstants.MULTI_CONFIG_PREFIX, MultiOssProperties.class).get();
来进行bind。
protected final <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context, boolean allowRecursiveBinding) { context.clearConfigurationProperty();
try {
target = handler.onStart(name, target, context);
if (target == null) {
return null;
}
Object bound = bindObject(name, target, handler, context,allowRecursiveBinding);
return handleBindResult(name, target, handler, context, bound);
} catch (Exception ex) {
return handleBindError(name, target, handler, context, ex);
}
}
如果我们的key是:oss.multi.clients.accout.xxx
实际上对应的是Map,那么它的引用名字就是clients。具体的key就是accout,那么对应的value就是OssProperties。
private Object bindBean(ConfigurationPropertyName name, Bindable<?> target, BindHandler handler, Context context, boolean allowRecursiveBinding) {
if (containsNoDescendantOf(context.getSources(), name)
|| isUnbindableBean(name, target, context)) {
return null;
}
BeanPropertyBinder propertyBinder = (propertyName, propertyTarget) -> bind(
name.append(propertyName), propertyTarget, handler, context, false);
Class<?> type = target.getType().resolve(Object.class);
if (!allowRecursiveBinding && context.hasBoundBean(type)) {
return null;
}
return context.withBean(type, () -> {
Stream<?> boundBeans = BEAN_BINDERS.stream()
.map((b) -> b.bind(name, target, context, propertyBinder));
return boundBeans.filter(Objects::nonNull).findFirst().orElse(null);
});
}
具体的一个bind情况。
private static final List<BeanBinder> BEAN_BINDERS;static {
List<BeanBinder> binders = new ArrayList<>();
binders.add(new JavaBeanBinder());
BEAN_BINDERS = Collections.unmodifiableList(binders);
}
public <T> T bind(ConfigurationPropertyName name, Bindable<T> target, Context context,
BeanPropertyBinder propertyBinder) {
boolean hasKnownBindableProperties = hasKnownBindableProperties(name, context);
Bean<T> bean = Bean.get(target, hasKnownBindableProperties);
if (bean == null) {
return null;
}
BeanSupplier<T> beanSupplier = bean.getSupplier(target);
boolean bound = bind(propertyBinder, bean, beanSupplier);
return (bound ? beanSupplier.get() : null);
}
// 返回对应的对象
public BeanSupplier<T> getSupplier(Bindable<T> target) {
return new BeanSupplier<>(() -> {
T instance = null;
if (target.getValue() != null) {
instance = target.getValue().get();
}
if (instance == null) {
instance = (T) BeanUtils.instantiateClass(this.resolvedType);
}
return instance;
});
}
参考地址
- https://blog.csdn.net/sayyy/article/details/83657119
如果大家喜欢我的文章,可以关注个人订阅号。欢迎随时留言、交流。如果想加入微信群的话一起讨论的话,请加管理员简栈文化-小助手(lastpass4u),他会拉你们进群。
以上是 配置文件@ConfigurationProperties读取List、Map参数 的全部内容, 来源链接: utcz.com/z/516203.html