application.yml
或者application.properties
中配置的属性绑定到某个类对应的属性上@Value
或@ConfigurationProperties
这种方式就不多说了,使用比较简单,但是局限性也比较大,比如只能在容器启动过程中的特定阶段进行绑定,如果容器启动好了或者容器正常运行中,再想去将动态读取到的配置属性绑定到某个对象上,那么@Value
或@ConfigurationProperties
是做不到的
@Value
或@ConfigurationProperties
标识的类上),此时我们需要将properties配置文件里的某些属性读取出来(映射到某个对象上),BeanDefinitionRegistry
+ 动态读取配置文件配置的多个数据源配置】来动态的向容器里注册自定义数据源的bean定义信息,以便spring启动时能将这些自定义的数据源注入容器org.springframework.boot.context.properties.bind.Binder
来进行绑定,但是springboot1.x中并没有这么方便的Binder,所以需要我们自己改造一下比如:在容器已经启动完成并且运行过程中将如下配置绑定到SmartPoolProperties
对象上
# 动态线程池之重试线程池配置
smart.pool.config.executors.retryExecutor.corePoolSize=1
smart.pool.config.executors.retryExecutor.maximumPoolSize=5
smart.pool.config.executors.retryExecutor.queueCapacity=256
smart.pool.config.executors.retryExecutor.keepAliveTime=30
smart.pool.config.executors.retryExecutor.threadNamePrefix=retry-executor
smart.pool.config.executors.retryExecutor.awaitTerminationSeconds=30
smart.pool.config.executors.retryExecutor.rejectedExecutionHandler=AbortPolicy
# 动态线程池之订单线程池配置
smart.pool.config.executors.orderExecutor.corePoolSize=1
smart.pool.config.executors.orderExecutor.maximumPoolSize=5
smart.pool.config.executors.orderExecutor.queueCapacity=256
smart.pool.config.executors.orderExecutor.keepAliveTime=30
smart.pool.config.executors.orderExecutor.threadNamePrefix=order-executor
smart.pool.config.executors.orderExecutor.awaitTerminationSeconds=30
smart.pool.config.executors.orderExecutor.rejectedExecutionHandler=AbortPolicy
# 动态线程池之会员线程池配置
smart.pool.config.executors.customerExecutor.corePoolSize=1
smart.pool.config.executors.customerExecutor.maximumPoolSize=5
smart.pool.config.executors.customerExecutor.queueCapacity=256
smart.pool.config.executors.customerExecutor.keepAliveTime=30
smart.pool.config.executors.customerExecutor.threadNamePrefix=customer-executor
smart.pool.config.executors.customerExecutor.awaitTerminationSeconds=30
smart.pool.config.executors.customerExecutor.rejectedExecutionHandler=AbortPolicy
public class SmartPoolProperties implements InitializingBean {
/**
* 线程池配置集合
*/
private Map<String, ThreadPoolProperties> executors;
/**
* 配置文件类型(用于刷新线程池配置)
*
* @see ConfigFileTypeEnum
*/
private String configFileType = ConfigFileTypeEnum.PROPERTIES.getValue();
@Override
public void afterPropertiesSet() throws Exception {
if (Objects.isNull(executors)) {
return;
}
executors.forEach((threadPoolName, properties) -> {
String poolName = properties.getThreadPoolName();
if (StringUtils.isNotBlank(poolName) && !poolName.equals(threadPoolName)) {
throw new SmartPoolExecutorException(String.format("threadPoolName is different, " +
"the first is [%s] and the second is [%s]", threadPoolName, poolName));
}
properties.setThreadPoolName(threadPoolName);
});
}
}
ThreadpoolProperties
public class ThreadPoolProperties {
/**
* 核心线程数,默认5
*/
protected int corePoolSize = 5;
/**
* 最大线程数,默认20
*/
protected int maximumPoolSize = 20;
/**
* 队列容量,默认1024 (一旦确定,禁止更新)
*/
protected int queueCapacity = 1024;
/**
* 保活秒数,默认300s
*/
protected long keepAliveTime = 300;
/**
* Timeout unit.
*/
private TimeUnit unit = TimeUnit.SECONDS;
/**
* 线程池拒绝策略名称,默认 {@link RejectedTypeEnum#CALLER_RUNS_POLICY}
*/
protected String rejectedExecutionHandler = RejectedTypeEnum.CALLER_RUNS_POLICY.getName();
/**
* 允许核心线程超时,默认false
*/
protected boolean allowCoreThreadTimeOut = false;
}
org.springframework.boot.bind.PropertySourcesBinder#bindTo
MapPropertySource
,可以直接使用springboot提供的org.springframework.core.env.MapPropertySource
即可org.springframework.core.env.MapPropertySource
,因此自己拓展了一下 org.springframework.core.env.MapPropertySource
/**
* {@link org.springframework.core.env.PropertySource} that reads keys and values from a {@code Map} object.
*
* @author wenpanfeng
* @see org.springframework.core.env.PropertiesPropertySource
*/
static class MapPropertySource extends EnumerablePropertySource<Map<Object, Object>> {
public MapPropertySource(String name, Map<Object, Object> source) {
super(name, source);
}
@Override
public Object getProperty(@NonNull String name) {
return source.get(name);
}
@Override
public boolean containsProperty(String name) {
return source.containsKey(name);
}
@NonNull
@Override
public String[] getPropertyNames() {
Set<Object> set = source.keySet();
if (CollectionUtils.isNotEmpty(set)) {
String[] result = new String[set.size()];
Object[] objects = set.toArray();
for (int i = 0; i < objects.length; i++) {
result[i] = objects[i].toString();
}
return result;
}
return new String[0];
}
}
PropertiesBinder
可以方便的将ConfigurableEnvironment
里的属性按照指定前缀绑定到指定的对象上/**
* springboot1.x属性绑定器
*
* @author wenpan 2022/11/23 10:57
*/
@Slf4j
public class PropertiesBinder {
private PropertiesBinder() {
}
/**
* 绑定属性到target
*
* @param properties properties
* @param prefix 绑定前缀
* @param target 目标对象
* @author wenpan 2022/11/24 17:05
*/
public static <T> void bindProperties(Map<Object, Object> properties, String prefix, T target) {
try {
log.info("------------>>>>>>>>>> start bind properties, prefix is {}, target is {}", prefix, target);
MapPropertySource mapPropertySource = new MapPropertySource("PropertiesBinder.bindProperties", properties);
MutablePropertySources propertySources = new MutablePropertySources();
propertySources.addLast(mapPropertySource);
PropertySourcesBinder binder = new PropertySourcesBinder(propertySources);
binder.bindTo(prefix, target);
log.info("------------>>>>>>>>>> end bind properties, prefix is {}, target is {}", prefix, target);
} catch (Exception ex) {
log.info("------------>>>>>>>>>> error bind properties, prefix is {}, target is {}", prefix, target);
throw new RuntimeException(String.format("Bind properties failed, prefix is [%s], target is [%s]", prefix, target));
}
}
/**
* 属性绑定
*
* @param configurableEnvironment Environment
* @param prefix 属性前缀
* @param clazz target clazz
* @return T
* @author wenpan 2022/11/23 11:04
*/
public static <T> T bindProperties(ConfigurableEnvironment configurableEnvironment, String prefix, Class<T> clazz) {
try {
log.info("------------>>>>>>>>>> start bind properties, prefix is {}, clazz is {}", prefix, clazz);
PropertySourcesBinder propertySourcesBinder = new PropertySourcesBinder(configurableEnvironment);
T instance = clazz.newInstance();
propertySourcesBinder.bindTo(prefix, instance);
log.info("------------>>>>>>>>>> end bind properties, prefix is {}, clazz is {}", prefix, clazz);
return instance;
} catch (Exception ex) {
log.info("------------>>>>>>>>>> error bind properties, prefix is {}, clazz is {}", prefix, clazz);
throw new RuntimeException(String.format("Bind properties failed, prefix is [%s], Class is [%s]", prefix, clazz));
}
}
Binder
,直接使用即可,不过多介绍!!!/**
*
* springboot2.x提供的属性绑定器
*
*
* @author wenpan 2022/12/10 1:34 下午
*/
public class PropertiesBinder2X {
private PropertiesBinder2X() {
}
/**
*
* 从 Map类型的properties 中按照prefix查找属性值并绑定到targetObj对象的对应属性上
* properties 可以是自己从配置文件中读取的key-value键值对
*
*
* @param properties map
* @param prefix 前缀
* @param targetObj 目标对象
* @author wenpan 2022/12/10 1:33 下午
*/
public static <T> void bindDtpProperties(Map<?, Object> properties, String prefix, T targetObj) {
ConfigurationPropertySource sources = new MapConfigurationPropertySource(properties);
Binder binder = new Binder(sources);
ResolvableType type = ResolvableType.forClass(targetObj.getClass());
Bindable<?> target = Bindable.of(type).withExistingValue(targetObj);
binder.bind(prefix, target);
}
/**
*
* 从environment中按照prefix查找属性值并绑定到targetObj对象的对应属性上
*
*
* @param environment environment
* @param prefix 前缀
* @param targetObj 目标对象
* @author wenpan 2022/12/10 1:32 下午
*/
public static <T> void bindDtpProperties(Environment environment, String prefix, T targetObj) {
Binder binder = Binder.get(environment);
ResolvableType type = ResolvableType.forClass(targetObj.getClass());
Bindable<?> target = Bindable.of(type).withExistingValue(targetObj);
binder.bind(prefix, target);
}
}