当前位置: 首页 > news >正文

玩转springboot之springboot属性绑定原理

属性绑定原理

注意:使用版本为spring-boot-2.2.2.RELEASE

在进行自定义配置的时候,我们通常使用@ConfigurationProperties注解来进行配置文件和配置类的映射,为什么可以映射呢?

主要靠的是@EnableConfigurationProperties注解来进行自动的将外部配置绑定到@ConfigurationProperties标注的类的属性中

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties

看到该注解上引入了一个EnableConfigurationPropertiesRegistrar类,这个应该就是关键了

class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {registerInfrastructureBeans(registry);ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);// 将配置的XxxProperties注册到spring容器中getTypes(metadata).forEach(beanRegistrar::register);}// @EnableConfigurationProperties注解中配置的value值,比如@EnableConfigurationProperties(ServerProperties.class),那么得到的值是ServerProperties.classprivate Set<Class<?>> getTypes(AnnotationMetadata metadata) {return metadata.getAnnotations().stream(EnableConfigurationProperties.class).flatMap((annotation) -> Arrays.stream(annotation.getClassArray(MergedAnnotation.VALUE))).filter((type) -> void.class != type).collect(Collectors.toSet());}@SuppressWarnings("deprecation")static void registerInfrastructureBeans(BeanDefinitionRegistry registry) {// 将ConfigurationPropertiesBindingPostProcessor注册到spring容器中,ConfigurationPropertiesBindingPostProcessor用于属性绑定ConfigurationPropertiesBindingPostProcessor.register(registry);ConfigurationPropertiesBeanDefinitionValidator.register(registry);ConfigurationBeanFactoryMetadata.register(registry);}}

在代码中我们看到了其向spring容器中注册了ConfigurationPropertiesBindingPostProcessor后置处理器,看该类的名字像是进行属性绑定的,来看一下该类的代码逻辑是如何进行属性绑定的

// 只展示了关键方法,其他方法没有展示
public class ConfigurationPropertiesBindingPostProcessorimplements BeanPostProcessor, PriorityOrdered, ApplicationContextAware, InitializingBean {@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);}}
}// ConfigurationPropertiesBinder类
BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {// 返回一个绑定了XxxProperties类的Bindable对象target,这个target对象即被外部属性值注入的目标对象Bindable<?> target = propertiesBean.asBindTarget();// 得到@ConfigurationProperties注解ConfigurationProperties annotation = propertiesBean.getAnnotation();// 得到BindHandler对象(默认是IgnoreTopLevelConverterNotFoundBindHandler对象),// 用于对ConfigurationProperties注解的ignoreUnknownFields等属性的处理BindHandler bindHandler = getBindHandler(target, annotation);// 得到一个Binder对象,并利用其bind方法执行外部属性绑定逻辑return getBinder().bind(annotation.prefix(), target, bindHandler);}// Binder类
private <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context,boolean allowRecursiveBinding, boolean create) {// 清空Binder的configurationProperty属性值context.clearConfigurationProperty();try {// 调用BindHandler的onStart方法,执行一系列的责任链对象的该方法Bindable<T> replacementTarget = handler.onStart(name, target, context);if (replacementTarget == null) {return handleBindResult(name, target, handler, context, null, create);}target = replacementTarget;// 调用bindObject方法对Bindable对象target的属性进行绑定外部配置的值,并返回赋值给bound对象Object bound = bindObject(name, target, handler, context, allowRecursiveBinding);// 封装handleBindResult对象并返回,注意在handleBindResult的构造函数中会调用BindHandler的onSucess,onFinish方法return handleBindResult(name, target, handler, context, bound, create);}catch (Exception ex) {return handleBindError(name, target, handler, context, ex);}}

绑定对象的真正操作在bindObject方法中

private <T> Object bindObject(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,Context context, boolean allowRecursiveBinding) {// 从propertySource中的配置属性,获取ConfigurationProperty对象property即application.properties配置文件中若有相关的配置的话,那么property将不会为nullConfigurationProperty property = findProperty(name, context);// 若property为null,则不会执行后续的属性绑定相关逻辑if (property == null && containsNoDescendantOf(context.getSources(), name) && context.depth != 0) {return null;}// 根据target类型获取不同的Binder,可以是null(普通的类型一般是Null),MapBinder,CollectionBinder或ArrayBinderAggregateBinder<?> aggregateBinder = getAggregateBinder(target, context);// 若aggregateBinder不为null,则调用bindAggregate并返回绑定后的对象if (aggregateBinder != null) {return bindAggregate(name, target, handler, context, aggregateBinder);}// 若property不为nullif (property != null) {try {// 绑定属性到对象中,比如配置文件中设置了server.port=8888,那么将会最终调用bindProperty方法进行属性设置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;}}// 只有@ConfigurationProperties注解的类进行外部属性绑定才会走这里return bindDataObject(name, target, handler, context, allowRecursiveBinding);}private Object bindDataObject(ConfigurationPropertyName name, Bindable<?> target, BindHandler handler,Context context, boolean allowRecursiveBinding) {if (isUnbindableBean(name, target, context)) {return null;}Class<?> type = target.getType().resolve(Object.class);if (!allowRecursiveBinding && context.isBindingDataObject(type)) {return null;}// 新建一个DataObjectPropertyBinder的实现类对象,注意这个对象实现了bindProperty方法DataObjectPropertyBinder propertyBinder = (propertyName, propertyTarget) -> bind(name.append(propertyName),propertyTarget, handler, context, false, false);return context.withDataObject(type, () -> {for (DataObjectBinder dataObjectBinder : this.dataObjectBinders) {// 真正实现将外部配置属性绑定到@ConfigurationProperties注解的XxxProperties类的属性中的逻辑Object instance = dataObjectBinder.bind(name, target, context, propertyBinder);if (instance != null) {return instance;}}return null;});}

https://zhhll.icu/2021/框架/springboot/源码/3.属性绑定原理/



喜欢的朋友记得点赞、收藏、关注哦!!!


http://www.mrgr.cn/news/59737.html

相关文章:

  • nacos的原理,为什么可以作为注册中心,和zookeeper的区别
  • tkinter的标准属性和集合管理
  • 【含开题报告+文档+PPT+源码】基于SpringBoot爱之屋摄影预约管理系统的设计与实现
  • 搭建 mongodb 副本集,很详细
  • 通过js控制css变量
  • 2024 BuildCTF 公开赛|MISC
  • 【C++奇遇记】C++中的基础知识(缺省参数,函数重载,引用)
  • 二进制搭建 Kubernetes v1.20
  • Kubernetes实战——DevOps集成SpringBoot项目
  • 深入了解嵌入式硬件设计
  • SSM-Springboot笔记(2)- SpringBoot常用开发技能
  • 自拍照片P西装领带的正装,用手机就可以搞定的方法
  • 二分查找法
  • linux-i2c驱动-ap3216c
  • 电机学习-SVPWM合成原理
  • InnoDB 存储引擎<二>页结构和行结构
  • 辣椒病害检测与分类数据集(猫脸码客 第226期 )
  • 代码随想录算法训练营第四十六天 | 188.买卖股票的最佳时机IV、309.最佳买卖股票时机含冷冻期 、714.买卖股票的最佳时机含手续费
  • PyCharm虚拟环境解释器问题:Python packaging tools not found.Install packaging tools
  • 【内网攻防】内网穿透隐秘隧道搭建
  • 【计网】网络层路由过程 ,理解IP分片与组装
  • Java 设计秒杀系统
  • 【万兴科技-注册_登录安全分析报告】
  • Next.js、Prisma 和 MySQL 实践示例
  • 深度解析百度搜索引擎点击结果:如何提高网站曝光率和用户满意度
  • TypeScript(中)+算法(二)