springboot1.x和2.x将配置属性绑定到对象上
admin
2024-03-15 20:26:31
0

一、问题描述

1、描述

  • 在基于springboot进行封装自定义框架或对某个开源框架进行二次改造时我们经常会涉及到将application.yml或者application.properties中配置的属性绑定到某个类对应的属性上
  • 使用@Value@ConfigurationProperties这种方式就不多说了,使用比较简单,但是局限性也比较大,比如只能在容器启动过程中的特定阶段进行绑定,如果容器启动好了或者容器正常运行中,再想去将动态读取到的配置属性绑定到某个对象上,那么@Value@ConfigurationProperties是做不到的
    • 比如:监听到配置中心配置发生变更,此时我们需要将变更的配置绑定到某个对象上(或者替换environment对象里的某个属性值)
    • 比如:在容器启动过程中(配置属性还没有绑定到@Value@ConfigurationProperties标识的类上),此时我们需要将properties配置文件里的某些属性读取出来(映射到某个对象上),
    • 比如:【多数据源组件开发】在spring启动过程中利用【BeanDefinitionRegistry + 动态读取配置文件配置的多个数据源配置】来动态的向容器里注册自定义数据源的bean定义信息,以便spring启动时能将这些自定义的数据源注入容器
  • 所以我们需要一个灵活的配置属性 <—> Java对象的映射工具类,在springboot2.x中提供了非常方便的org.springframework.boot.context.properties.bind.Binder来进行绑定,但是springboot1.x中并没有这么方便的Binder,所以需要我们自己改造一下

2、示例

比如:在容器已经启动完成并且运行过程中将如下配置绑定到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 executors;/*** 配置文件类型(用于刷新线程池配置)** @see ConfigFileTypeEnum*/private String configFileType = ConfigFileTypeEnum.PROPERTIES.getValue();@Overridepublic 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;
}

二、springboot1.x绑定方式

  • 具体原理可以参照springboot里如何将environment对象里属性绑定到指定对象上即可,可以看看org.springframework.boot.bind.PropertySourcesBinder#bindTo

1、定义一个MapPropertySource

  • 如果你的map中key类型是string的话,那么可以不用自定义下面这个MapPropertySource,可以直接使用springboot提供的org.springframework.core.env.MapPropertySource即可
  • 我这里是由于业务需要,Map里的key必须是Object类型,所以无法直接使用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*/
public class MapPropertySource extends EnumerablePropertySource> {public MapPropertySource(String name, Map source) {super(name, source);}@Overridepublic Object getProperty(String name) {return this.source.get(name);}@Overridepublic boolean containsProperty(String name) {return this.source.containsKey(name);}@Overridepublic String[] getPropertyNames() {Set set = this.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];}}
 

2、自定义PropertiesBinder

  • PropertiesBinder可以方便的将ConfigurableEnvironment里的属性按照指定前缀绑定到指定的对象上
  • 也可以将properties文件或yaml文件里的属性解析为Map集合后,将Map集合里的key-value按照指定的前缀绑定到指定的对象上
/*** 属性绑定器** @author wenpanfeng 2022/11/23 10:57*/
@Slf4j
public class PropertiesBinder {private PropertiesBinder() {}/*** 绑定smart pool properties** @param configurableEnvironment configurableEnvironment* @return SmartPoolProperties* @author wenpanfeng 2022/11/23 11:12*/public static SmartPoolProperties bindSmartPoolProperties(ConfigurableEnvironment configurableEnvironment) {return bindProperties(configurableEnvironment, PrefixConst.ExecutorConfig.SMART_POOL_PREFIX, SmartPoolProperties.class);}/*** 绑定properties to smartPoolProperties** @param properties          properties* @param smartPoolProperties smartPoolProperties* @author wenpanfeng 2022/11/24 21:15*/public static void bindSmartProperties(Map properties, SmartPoolProperties smartPoolProperties) {bindProperties(properties, PrefixConst.ExecutorConfig.SMART_POOL_PREFIX, smartPoolProperties);}/*** 绑定属性到target** @param properties properties* @param prefix     绑定前缀* @param target     目标对象* @author wenpanfeng 2022/11/24 17:05*/public static  void bindProperties(Map properties, String prefix, T target) {try {log.info("------------>>>>>>>>>> start bind properties, prefix is {}, target is {}", prefix, target);MapPropertySource mapPropertySource = new MapPropertySource("PropertiesBinder.bindSmartProperties", 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 PropertiesBindException(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 wenpanfeng 2022/11/23 11:04*/public static  T bindProperties(ConfigurableEnvironment configurableEnvironment, String prefix, Class 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 PropertiesBindException(String.format("Bind properties failed, prefix is [%s], Class is [%s]", prefix, clazz));}}
}

三、springboot2.x绑定方式

  • springboot2.x就非常方便了,官方提供了Binder,直接使用即可,不过多介绍!!!
public class PropertiesBinder {private PropertiesBinder() {}public static void bindDtpProperties(Map properties, DtpProperties dtpProperties) {ConfigurationPropertySource sources = new MapConfigurationPropertySource(properties);Binder binder = new Binder(sources);ResolvableType type = ResolvableType.forClass(DtpProperties.class);Bindable target = Bindable.of(type).withExistingValue(dtpProperties);binder.bind(MAIN_PROPERTIES_PREFIX, target);}public static void bindDtpProperties(Environment environment, DtpProperties dtpProperties) {Binder binder = Binder.get(environment);ResolvableType type = ResolvableType.forClass(DtpProperties.class);Bindable target = Bindable.of(type).withExistingValue(dtpProperties);binder.bind(MAIN_PROPERTIES_PREFIX, target);}
}

相关内容

热门资讯

原创 别... 在繁忙的都市生活中,我们常常渴望寻找一种简单而美味的点心来慰藉自己的心灵。今天,我将与大家分享一道简...
原创 到... 标题:到新开的饭馆吃饭,老板说吃完打七折,上菜后知道他为啥奸笑了! 在美食的世界里,每一次用餐都是...
年轻人为什么偏爱“市井小店”? 图为正在沣元春饼馆内用餐的消费者。 “我是冲着‘必吃榜’的名头过来的。这家店虽然位置隐蔽,七拐八拐才...
原创 1... 标题:1把韭菜1把粉条,做成饼,竟如此快手又好吃,早上不用只啃面包了。 在忙碌的早晨,我们总是渴望...
原创 吐... 吐司,这个看似简单的早餐选择,其实蕴含着无限的可能性。今天,我将带领大家探索一种无需手套膜也能拉丝的...
精彩的近义词。 精彩的近义词。精彩近义词:出色,漂亮
朵拉小羊在羊奶粉排行第几,请问... 朵拉小羊在羊奶粉排行第几,请问宝妈们这款奶粉怎么样?朵拉小羊在羊奶粉排行榜中的排名一直都挺靠前的,挺...
塞尔达传说荒野之息怎么获得武器... 塞尔达传说荒野之息怎么获得武器?前期武器入手方法一览《塞尔达传说:荒野之息》很多玩家都吐槽赠送武器坏...
口袋妖怪复刻精灵性格能改变吗? 口袋妖怪复刻精灵性格能改变吗?口袋妖怪复刻精灵性格能改变哦,后期可能通过进化来改变哦固定交换能哪来刷...
女人爱上一个人和男人爱上一个人... 女人爱上一个人和男人爱上一个人,有哪些不一样吗?男人和女人是两个完全不同的有机体,在思维、心理和行为...
《哆啦A梦》中哪个片段让你感动... 《哆啦A梦》中哪个片段让你感动?每一集都有精彩的一部分哆啦A 梦真的是陪伴了大雄很久哆啦A梦要回去的...
求不祥之刃符文选择 求不祥之刃符文选择红法穿,蓝减cd,黄成长血,精华法穿,不详前期qe技能陪和法术穿透是很强势的,应为...
超级教师是什么台播放的 超级教师是什么台播放的,乐视TV电视台放超级教师是乐视出品,只能在乐视TV看!
有什么搞笑好看的鬼片? 有什么搞笑好看的鬼片?韩国片主君的太阳!
摘橘子沈从文中夭夭是一个什么样... 摘橘子沈从文中夭夭是一个什么样的女孩摘橘子沈从文中夭夭是一个什么样的女孩?相关内容如下:夭夭是《边城...
作文题目:童心荟萃.主题;节水... 作文题目:童心荟萃.主题;节水。爱水。护水~~╮(╯▽╰)╭ ...
我可不可以和姐夫家的堂兄弟结婚... 我可不可以和姐夫家的堂兄弟结婚?这个是没问题的,不是三代内有血亲关系的都可以
原生家庭对人的影响很大,如何摆... 原生家庭对人的影响很大,如何摆脱原生家庭所造成的性格缺陷?在学校里好好学习,尽量不要被原生家庭的坏习...
你知道哪些珍惜时间的名人故事吗... 你知道哪些珍惜时间的名人故事吗?1、悬梁刺股东汉时候,有个人名叫孙敬,是著名的政治家。他年轻时勤奋好...
有一首歌,歌词里铿锵玫瑰,作何... 有一首歌,歌词里铿锵玫瑰,作何解释?铿 锵 玫 瑰"铿锵"意味着声音宏亮,节奏分明;"玫瑰"代表着美...