聊聊springboot的ConfigurationProperties的绑定
序
本文主要研究一下springboot的ConfigurationProperties的绑定
ConfigurationPropertiesBindingPostProcessor
org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java
/*** {@link BeanPostProcessor} to bind {@link PropertySources} to beans annotated with* {@link ConfigurationProperties @ConfigurationProperties}.** @author Dave Syer* @author Phillip Webb* @author Christian Dupuis* @author Stephane Nicoll* @author Madhura Bhave* @since 1.0.0*/
public class ConfigurationPropertiesBindingPostProcessorimplements BeanPostProcessor, PriorityOrdered, ApplicationContextAware, InitializingBean {/*** The bean name that this post-processor is registered with.*/public static final String BEAN_NAME = ConfigurationPropertiesBindingPostProcessor.class.getName();private ApplicationContext applicationContext;private BeanDefinitionRegistry registry;private ConfigurationPropertiesBinder binder;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}@Overridepublic void afterPropertiesSet() throws Exception {// We can't use constructor injection of the application context because// it causes eager factory bean initializationthis.registry = (BeanDefinitionRegistry) this.applicationContext.getAutowireCapableBeanFactory();this.binder = ConfigurationPropertiesBinder.get(this.applicationContext);}@Overridepublic int getOrder() {return Ordered.HIGHEST_PRECEDENCE + 1;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));return bean;}private void bind(ConfigurationPropertiesBean bean) {if (bean == null || hasBoundValueObject(bean.getName())) {return;}Assert.state(bean.getBindMethod() == BindMethod.JAVA_BEAN, "Cannot bind @ConfigurationProperties for bean '"+ bean.getName() + "'. Ensure that @ConstructorBinding has not been applied to regular bean");try {this.binder.bind(bean);}catch (Exception ex) {throw new ConfigurationPropertiesBindException(bean, ex);}}private boolean hasBoundValueObject(String beanName) {return this.registry.containsBeanDefinition(beanName) && this.registry.getBeanDefinition(beanName) instanceof ConfigurationPropertiesValueObjectBeanDefinition;}/*** Register a {@link ConfigurationPropertiesBindingPostProcessor} bean if one is not* already registered.* @param registry the bean definition registry* @since 2.2.0*/public static void register(BeanDefinitionRegistry registry) {Assert.notNull(registry, "Registry must not be null");if (!registry.containsBeanDefinition(BEAN_NAME)) {BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(ConfigurationPropertiesBindingPostProcessor.class,ConfigurationPropertiesBindingPostProcessor::new).getBeanDefinition();definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registry.registerBeanDefinition(BEAN_NAME, definition);}ConfigurationPropertiesBinder.register(registry);}}
ConfigurationPropertiesBindingPostProcessor实现了BeanPostProcessor、PriorityOrdered、ApplicationContextAware、InitializingBean四个接口;其getOrder方法返回的是
Ordered.HIGHEST_PRECEDENCE + 1即仅次于最高的优先级;其postProcessBeforeInitialization方法主要是执行bind方法(先通过ConfigurationPropertiesBean.get获取ConfigurationPropertiesBean,再通过binder进行bind);其afterPropertiesSet主要是获取BeanDefinitionRegistry与ConfigurationPropertiesBinder
ConfigurationPropertiesBean.get
org/springframework/boot/context/properties/ConfigurationPropertiesBean.java
public static ConfigurationPropertiesBean get(ApplicationContext applicationContext, Object bean, String beanName) {Method factoryMethod = findFactoryMethod(applicationContext, beanName);return create(beanName, bean, bean.getClass(), factoryMethod);}private static ConfigurationPropertiesBean create(String name, Object instance, Class<?> type, Method factory) {ConfigurationProperties annotation = findAnnotation(instance, type, factory, ConfigurationProperties.class);if (annotation == null) {return null;}Validated validated = findAnnotation(instance, type, factory, Validated.class);Annotation[] annotations = (validated != null) ? new Annotation[] { annotation, validated }: new Annotation[] { annotation };ResolvableType bindType = (factory != null) ? ResolvableType.forMethodReturnType(factory): ResolvableType.forClass(type);Bindable<Object> bindTarget = Bindable.of(bindType).withAnnotations(annotations);if (instance != null) {bindTarget = bindTarget.withExistingValue(instance);}return new ConfigurationPropertiesBean(name, instance, annotation, bindTarget);}
get方法主要是获取工厂方法,之后获取annotation,获取bindTarget,最后创建ConfigurationPropertiesBean
ConfigurationPropertiesBean
org/springframework/boot/context/properties/ConfigurationPropertiesBean.java
/*** Provides access to {@link ConfigurationProperties @ConfigurationProperties} bean* details, regardless of if the annotation was used directly or on a {@link Bean @Bean}* factory method. This class can be used to access {@link #getAll(ApplicationContext)* all} configuration properties beans in an ApplicationContext, or* {@link #get(ApplicationContext, Object, String) individual beans} on a case-by-case* basis (for example, in a {@link BeanPostProcessor}).** @author Phillip Webb* @since 2.2.0* @see #getAll(ApplicationContext)* @see #get(ApplicationContext, Object, String)*/
public final class ConfigurationPropertiesBean {private final String name;private final Object instance;private final ConfigurationProperties annotation;private final Bindable<?> bindTarget;private final BindMethod bindMethod;//......
}
ConfigurationPropertiesBean用于代表一个标注了
@ConfigurationProperties注解的bean的信息
ConfigurationPropertiesBinder
org/springframework/boot/context/properties/ConfigurationPropertiesBinder.java
/*** Internal class used by the {@link ConfigurationPropertiesBindingPostProcessor} to* handle the actual {@link ConfigurationProperties @ConfigurationProperties} binding.** @author Stephane Nicoll* @author Phillip Webb*/
class ConfigurationPropertiesBinder {private static final String BEAN_NAME = "org.springframework.boot.context.internalConfigurationPropertiesBinder";private static final String FACTORY_BEAN_NAME = "org.springframework.boot.context.internalConfigurationPropertiesBinderFactory";private static final String VALIDATOR_BEAN_NAME = EnableConfigurationProperties.VALIDATOR_BEAN_NAME;private final ApplicationContext applicationContext;private final PropertySources propertySources;private final Validator configurationPropertiesValidator;private final boolean jsr303Present;private volatile Validator jsr303Validator;private volatile Binder binder;ConfigurationPropertiesBinder(ApplicationContext applicationContext) {this.applicationContext = applicationContext;this.propertySources = new PropertySourcesDeducer(applicationContext).getPropertySources();this.configurationPropertiesValidator = getConfigurationPropertiesValidator(applicationContext);this.jsr303Present = ConfigurationPropertiesJsr303Validator.isJsr303Present(applicationContext);}BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {Bindable<?> target = propertiesBean.asBindTarget();ConfigurationProperties annotation = propertiesBean.getAnnotation();BindHandler bindHandler = getBindHandler(target, annotation);return getBinder().bind(annotation.prefix(), target, bindHandler);}private Binder getBinder() {if (this.binder == null) {this.binder = new Binder(getConfigurationPropertySources(), getPropertySourcesPlaceholdersResolver(),getConversionService(), getPropertyEditorInitializer(), null,ConfigurationPropertiesBindConstructorProvider.INSTANCE);}return this.binder;}//......
}
ConfigurationPropertiesBinder的bind方法根据ConfigurationPropertiesBean的target与annotation取获取bindHandler,然后通过binder去执行bind方法
binder的构造器依赖了propertySources、placeholdersResolver、conversionService、propertyEditorInitializer、defaultBindHandler、constructorProvider
Binder
org/springframework/boot/context/properties/bind/Binder.java
private <T> Object bindObject(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,Context context, boolean allowRecursiveBinding) {ConfigurationProperty property = findProperty(name, context);if (property == null && context.depth != 0 && containsNoDescendantOf(context.getSources(), name)) {return null;}AggregateBinder<?> aggregateBinder = getAggregateBinder(target, context);if (aggregateBinder != null) {return bindAggregate(name, target, handler, context, aggregateBinder);}if (property != null) {try {return bindProperty(target, context, property);}catch (ConverterNotFoundException ex) {// We might still be able to bind it using the recursive bindersObject instance = bindDataObject(name, target, handler, context, allowRecursiveBinding);if (instance != null) {return instance;}throw ex;}}return bindDataObject(name, target, handler, context, allowRecursiveBinding);}private AggregateBinder<?> getAggregateBinder(Bindable<?> target, Context context) {Class<?> resolvedType = target.getType().resolve(Object.class);if (Map.class.isAssignableFrom(resolvedType)) {return new MapBinder(context);}if (Collection.class.isAssignableFrom(resolvedType)) {return new CollectionBinder(context);}if (target.getType().isArray()) {return new ArrayBinder(context);}return null;}private <T> Object bindAggregate(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,Context context, AggregateBinder<?> aggregateBinder) {AggregateElementBinder elementBinder = (itemName, itemTarget, source) -> {boolean allowRecursiveBinding = aggregateBinder.isAllowRecursiveBinding(source);Supplier<?> supplier = () -> bind(itemName, itemTarget, handler, context, allowRecursiveBinding, false);return context.withSource(source, supplier);};return context.withIncreasedDepth(() -> aggregateBinder.bind(name, target, elementBinder));}private <T> Object bindProperty(Bindable<T> target, Context context, ConfigurationProperty property) {context.setConfigurationProperty(property);Object result = property.getValue();result = this.placeholdersResolver.resolvePlaceholders(result);result = context.getConverter().convert(result, target);return result;}
bindObject方法先通过findProperty获取ConfigurationProperty,然后执行bindAggregate或者bindProperty;AggregateBinder主要是处理Map、Collection、Array类型;bindProperty方法这里从property获取绑定的值,然后resolvePlaceholders,最后通过converter的convert方法把值绑定到target上
BindConverter
org/springframework/boot/context/properties/bind/BindConverter.java
<T> T convert(Object value, ResolvableType type, Annotation... annotations) {if (value == null) {return null;}return (T) this.conversionService.convert(value, TypeDescriptor.forObject(value),new ResolvableTypeDescriptor(type, annotations));}
BindConverter的convert方法则是通过conversionService进行
小结
ConfigurationPropertiesBindingPostProcessor实现了BeanPostProcessor、PriorityOrdered、ApplicationContextAware、InitializingBean四个接口;其getOrder方法返回的是Ordered.HIGHEST_PRECEDENCE + 1即仅次于最高的优先级;其postProcessBeforeInitialization方法主要是执行bind方法(先通过ConfigurationPropertiesBean.get获取ConfigurationPropertiesBean,再通过binder进行bind);其afterPropertiesSet主要是获取BeanDefinitionRegistry与ConfigurationPropertiesBinder
相关文章:
聊聊springboot的ConfigurationProperties的绑定
序 本文主要研究一下springboot的ConfigurationProperties的绑定 ConfigurationPropertiesBindingPostProcessor org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java /*** {link BeanPostProcessor} to bind {link PropertySo…...
Mysql和Oracle的语法区别?
Mysql和Oracle是两种不同的关系型数据库。 MySQL通常在中小型应用程序、Web应用程序和小型企业中广泛使用,因为它易于学习和部署,而且成本较低。 Oracle数据库通常用于大型企业和复杂的企业级应用程序,因为它提供了高度可扩展性、高可用性…...
F - LIS on Tree
F - LIS on Tree (atcoder.jp) 问题描述:树上LIS。 普通LIS。O(n * n)。 void solve() {int n; cin>>n;vector<int> f(n 1),a(n1);for(int i 1; i < n; i) {cin>>a[i];f[i] 1;for(int j 1; j < i; j) {if(a[i] > a[j]) f[i] max…...
2023 年全国大学生数学建模B题目-多波束测线问题
B题目感觉属于平面几何和立体几何的问题,本质上需要推导几何变换情况,B题目属于有标准答案型,没太大的把握不建议选择,可发挥型不大。 第一问 比较简单,就一个2维平面的问题,但有点没理解,这个…...
qt creater11 翻译国际化教程教程:
先出效果图。 闲聊几句:qt这个翻译很方便,能直接导出项目里所有文字。 具体步骤如下: 在Qt中,我们可以使用QTranslator类来实现多语言切换。以下是一般步骤: 1. 在你的源代码中,所有需要翻译的字符串都…...
【AWS实验 】在 AWS Fargate 上使用 Amazon ECS 部署应用程序
文章目录 实验概览目标实验环境任务 1:连接到实验命令主机任务 2:将应用程序容器化任务 3:构建 Web2048 容器任务 4:创建 Amazon ECR 存储库并推送 Docker 映像任务 5:创建 ECS 集群任务 6:测试应用程序总结…...
matlab几种求解器的选择fsolve-sole-vpasolve
文章目录 fsolvesolvevpasovle总结vpasovle的结果fsovle的结果 fsolve 求数值解 result_xfsolve(my_fun,x0,options)参数: my_fun:待求解函数,作为一个.m文件 x0:初始值,向量,可以仅仅指定其中的几项solve 强大的求解器。在方程组中求解析…...
无限访问 GPT-4,OpenAI 强势推出 ChatGPT 企业版!
继 ChatGPT 收费大降价、推出 App 版等系列动作之后,OpenAI 于今日宣布正式发布面向企业的 AI 助手——ChatGPT Enterprise 版。 与 To C 端的 ChatGPT 版本有所不同的是,该版本可以以更快速度无限制地访问 GPT-4,还可以用来处理更长输入的上…...
MySQL的故事——Schema与数据类型优化
Schema与数据类型优化 一、选择优化的数据类型 更小的通常更好 应该尽量使用可以正确存储数据的最小类型,更小的数据类型通常更快,因为他们占用更少的磁盘,内存和CPU缓存,并且处理时需要的CPU周期更少 简单就好 更简单的数据类型…...
C++编译和链接
编译和链接 一、源代码的组织 头文件(.h):#include头文件、函数的声明、结构体的声明、类的声明、模板的声明、内联函数、#define和const定义的常量等。 源文件(.cpp):函数的定义、类的定义、模板具体化的…...
【CSDN技术】Markdown编辑器如何使用-csdn博客编写入门
Markdown编辑器如何使用-csdn博客编写入门 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自…...
【docker】运行redis
拉取redis镜像 有多种选择: redis(基础版)redis/redis-stack(包含redis stack server和RedisInsight)redis/redis-stack-server(仅包含redis stack server) docker pull redis docker pull r…...
Paddle训练COCO-stuff数据集学习记录
COCO-stuff数据集 COCO-Stuff数据集对COCO数据集中全部164K图片做了像素级的标注。 80 thing classes, 91 stuff classes and 1 class ‘unlabeled’ 数据集下载 wget --directory-prefixdownloads http://images.cocodataset.org/zips/train2017.zip wget --directory-prefi…...
SpringBoot 框架学习
java 学习笔记指路 基础知识 Python转java补充知识 Java中常见的名词解释 前端 【黑马程序员pink老师前端】HTML 【黑马程序员pink老师前端】JavaScript基础大总结 【黑马程序员pink老师前端】JavaScript函数与作用域 【黑马程序员pink老师前端】JavaScript对象 数据库 【黑马程…...
java - lua - redis 完成商品库存的删减
java调用lua脚本完成对商品库存的管理 主页链接 微风轻吟挽歌的主页 如若有帮助请帮忙点赞 //lua脚本 获取到内存不够的商品StringBuilder sb new StringBuilder();//定义一个数组存储可能缺少库存的值sb.append(" local table {} ");//获取值sb.append(" …...
dbeaver离线安装clickhouse连接驱动
Clickhouse 数据库连接工具——DBeaver 主要介绍了Clickhouse 数据库连接工具——DBeaver相关的知识,希望对你有一定的参考价值。 Clickhouse 数据库连接工具——DBeaver 1.下载 DBeaver 和 连接驱动 https://dbeaver.io/files/dbeaver-ce-latest-x86_64-setup.…...
2024腾讯校招后端面试真题汇总及其解答(二)
11.如果同时有5个任务在10分钟之后提交,或者更多,那么如果是一个个从队列中拿数据,那么前一个任务会影响后续任务执行时间,说一下解决思路 你的问题是一个典型的并发处理问题。如果你的系统是单线程的,那么的确,前一个任务的执行时间会影响后续任务的执行时间。但是,你…...
datagrip 相关数据连接信息无缝迁移
背景 因为公司换电脑了,接触的项目比较多,不同项目,不同环境的数据库连接有好几十个,如果在新电脑上挨个重新连接一遍劳心劳力,所以想看一下能不能直接将之前保存的连接信息直接迁移到新的电脑上面。 为此,…...
不就是G2O嘛
从零开始一起学习SLAM | 理解图优化,一步步带你看懂g2o代码 SLAM的后端一般分为两种处理方法,一种是以扩展卡尔曼滤波(EKF)为代表的滤波方法,一种是以图优化为代表的非线性优化方法。不过,目前SLAM研究的主…...
C#开发的OpenRA游戏之系统参数选项按钮
C#开发的OpenRA游戏之系统参数选项按钮 前面分析了信标按钮,从图上可以看到,靠右边的按钮,就是系统参数选项按钮: 这个按钮与前面三个按钮是不一样的,虽然它们在排列位置上是放在一起,但是处理的方法方式是不一样的,因为这个选项按钮,并不需要发命令给服务器,再返回来…...
Smara全栈框架解析:文件路由、服务端函数与类型安全实践
1. 项目概述:一个面向未来的全栈应用开发框架最近在GitHub上闲逛,发现了一个名为smara-io/smara的项目,它的star数增长得挺快,引起了我的注意。作为一个在Web开发领域摸爬滚打了十多年的老码农,我对各种框架、工具链的…...
skene-cookbook:700+AI技能库,一键部署Claude/Cursor提示词工程自动化
1. 项目概述:当AI技能库成为你的“瑞士军刀”如果你和我一样,每天都在和Claude、Cursor这类AI工具打交道,那你肯定遇到过这样的场景:想让它帮你分析一下营销数据,得先写一大段复杂的提示词;想让它检查代码安…...
Docker化Ollama部署指南:开箱即用的本地大模型服务方案
1. 项目概述:一个让Ollama“上手即用”的Docker镜像如果你最近在本地折腾过大语言模型,大概率听说过Ollama。它确实是个神器,把模型下载、加载、运行和API服务这些繁琐步骤打包成了一个简单的命令行工具,让在个人电脑上跑Llama、Q…...
探索下一代算法库:x-algorithm的设计理念与核心技术解析
1. 项目概述:算法库的“下一站” 最近在GitHub上看到一个挺有意思的项目,叫 NextFrontierBuilds/x-algorithm 。光看这个名字,就透着一股“前沿”和“探索”的味道。作为一个在算法和数据工程领域摸爬滚打了十来年的老码农,我对…...
如何用dedao-dl高效管理得到APP学习资源:完整实战指南
如何用dedao-dl高效管理得到APP学习资源:完整实战指南 【免费下载链接】dedao-dl 得到 APP 课程下载工具,可在终端查看文章内容,可生成 PDF,音频文件,markdown 文稿,可下载电子书。可结合 openclaw skill 等…...
终极指南:如何用开源工具免费获取八大网盘真实下载链接,告别客户端强制安装
终极指南:如何用开源工具免费获取八大网盘真实下载链接,告别客户端强制安装 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 /…...
开发者技能管理工具:从YAML定义到可视化部署的完整实践
1. 项目概述:一个面向开发者的技能管理工具最近在GitHub上看到一个挺有意思的项目,叫fightZy/simple-skills。乍一看名字,你可能会觉得这是个关于“简单技能”的什么教程或者清单。但点进去之后,我发现它的定位其实更偏向于一个个…...
别再当期刊 “陪跑者” 了!Paperxie 期刊写作,把投稿踩坑率降到最低
paperxie-免费查重复率aigc检测/开题报告/毕业论文/智能排版/文献综述/期刊论文https://www.paperxie.cn/ai/journalArticleshttps://www.paperxie.cn/ai/journalArticles 投刊的挫败感,从来都不是从拒信才开始的。你熬了几周写好的初稿,可能刚进初审就被…...
权威榜单2026年上海做小程序哪家好,实地测评这几家靠谱公司真心值得推荐
在2026年,选择合适的小程序开发公司是每个企业数字化转型的关键一步。上海的市场上有许多优秀的开发公司,它们各具特色,提供不同类型的服务。在这个权威榜单中,我们将向您介绍十家在技术实力、项目经验以及客户满意度等方面都有突…...
告别系统软键盘!手把手教你为Qt应用定制一个高颜值、全功能的虚拟键盘(支持Win/Linux)
告别系统软键盘!手把手教你为Qt应用定制一个高颜值、全功能的虚拟键盘(支持Win/Linux) 在工业控制、教育软件、信息发布系统等专业场景中,系统自带的软键盘往往难以满足定制化需求——风格突兀、功能单一、跨平台表现不一致。本文…...
