聊聊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游戏之系统参数选项按钮 前面分析了信标按钮,从图上可以看到,靠右边的按钮,就是系统参数选项按钮: 这个按钮与前面三个按钮是不一样的,虽然它们在排列位置上是放在一起,但是处理的方法方式是不一样的,因为这个选项按钮,并不需要发命令给服务器,再返回来…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...