玩转springboot之springboot属性绑定原理

打印 上一主题 下一主题

主题 689|帖子 689|积分 2071

属性绑定原理

   注意:使用版本为spring-boot-2.2.2.RELEASE
  在进行自界说设置的时间,我们通常使用@ConfigurationProperties注解来进行设置文件和设置类的映射,为什么可以映射呢?
主要靠的是@EnableConfigurationProperties注解来进行主动的将外部设置绑定到@ConfigurationProperties标注的类的属性中
  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Import(EnableConfigurationPropertiesRegistrar.class)
  5. public @interface EnableConfigurationProperties
复制代码
看到该注解上引入了一个EnableConfigurationPropertiesRegistrar类,这个应该就是关键了
  1. class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {
  2.    @Override
  3.    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
  4.       registerInfrastructureBeans(registry);
  5.       ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
  6.      // 将配置的XxxProperties注册到spring容器中
  7.       getTypes(metadata).forEach(beanRegistrar::register);
  8.    }
  9.   // @EnableConfigurationProperties注解中配置的value值,比如@EnableConfigurationProperties(ServerProperties.class),那么得到的值是ServerProperties.class
  10.    private Set<Class<?>> getTypes(AnnotationMetadata metadata) {
  11.       return metadata.getAnnotations().stream(EnableConfigurationProperties.class)
  12.             .flatMap((annotation) -> Arrays.stream(annotation.getClassArray(MergedAnnotation.VALUE)))
  13.             .filter((type) -> void.class != type).collect(Collectors.toSet());
  14.    }
  15.    @SuppressWarnings("deprecation")
  16.    static void registerInfrastructureBeans(BeanDefinitionRegistry registry) {
  17.      // 将ConfigurationPropertiesBindingPostProcessor注册到spring容器中,ConfigurationPropertiesBindingPostProcessor用于属性绑定
  18.       ConfigurationPropertiesBindingPostProcessor.register(registry);
  19.       ConfigurationPropertiesBeanDefinitionValidator.register(registry);
  20.       ConfigurationBeanFactoryMetadata.register(registry);
  21.    }
  22. }
复制代码
在代码中我们看到了其向spring容器中注册了ConfigurationPropertiesBindingPostProcessor后置处置惩罚器,看该类的名字像是进行属性绑定的,来看一下该类的代码逻辑是如何进行属性绑定的
  1. // 只展示了关键方法,其他方法没有展示
  2. public class ConfigurationPropertiesBindingPostProcessor
  3.       implements BeanPostProcessor, PriorityOrdered, ApplicationContextAware, InitializingBean {
  4.    @Override
  5.    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  6.      // 进行绑定
  7.       bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
  8.       return bean;
  9.    }
  10.    private void bind(ConfigurationPropertiesBean bean) {
  11.       if (bean == null || hasBoundValueObject(bean.getName())) {
  12.          return;
  13.       }
  14.       Assert.state(bean.getBindMethod() == BindMethod.JAVA_BEAN, "Cannot bind @ConfigurationProperties for bean '"
  15.             + bean.getName() + "'. Ensure that @ConstructorBinding has not been applied to regular bean");
  16.       try {
  17.          this.binder.bind(bean);
  18.       }
  19.       catch (Exception ex) {
  20.          throw new ConfigurationPropertiesBindException(bean, ex);
  21.       }
  22.    }
  23. }
  24. // ConfigurationPropertiesBinder类
  25. BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
  26.   // 返回一个绑定了XxxProperties类的Bindable对象target,这个target对象即被外部属性值注入的目标对象
  27.                 Bindable<?> target = propertiesBean.asBindTarget();
  28.   // 得到@ConfigurationProperties注解
  29.                 ConfigurationProperties annotation = propertiesBean.getAnnotation();
  30.   // 得到BindHandler对象(默认是IgnoreTopLevelConverterNotFoundBindHandler对象),
  31.    // 用于对ConfigurationProperties注解的ignoreUnknownFields等属性的处理
  32.                 BindHandler bindHandler = getBindHandler(target, annotation);
  33.           // 得到一个Binder对象,并利用其bind方法执行外部属性绑定逻辑
  34.                 return getBinder().bind(annotation.prefix(), target, bindHandler);
  35.         }
  36. // Binder类
  37. private <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context,
  38.                         boolean allowRecursiveBinding, boolean create) {
  39.   // 清空Binder的configurationProperty属性值
  40.                 context.clearConfigurationProperty();
  41.                 try {
  42.       // 调用BindHandler的onStart方法,执行一系列的责任链对象的该方法
  43.                         Bindable<T> replacementTarget = handler.onStart(name, target, context);
  44.                         if (replacementTarget == null) {
  45.                                 return handleBindResult(name, target, handler, context, null, create);
  46.                         }
  47.                         target = replacementTarget;
  48.       // 调用bindObject方法对Bindable对象target的属性进行绑定外部配置的值,并返回赋值给bound对象
  49.                         Object bound = bindObject(name, target, handler, context, allowRecursiveBinding);
  50.       // 封装handleBindResult对象并返回,注意在handleBindResult的构造函数中会调用BindHandler的onSucess,onFinish方法
  51.                         return handleBindResult(name, target, handler, context, bound, create);
  52.                 }
  53.                 catch (Exception ex) {
  54.                         return handleBindError(name, target, handler, context, ex);
  55.                 }
  56.         }
复制代码
绑定对象的真正操纵在bindObject方法中
  1. private <T> Object bindObject(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,
  2.                         Context context, boolean allowRecursiveBinding) {
  3.           // 从propertySource中的配置属性,获取ConfigurationProperty对象property即application.properties配置文件中若有相关的配置的话,那么property将不会为null
  4.                 ConfigurationProperty property = findProperty(name, context);
  5.   // 若property为null,则不会执行后续的属性绑定相关逻辑
  6.                 if (property == null && containsNoDescendantOf(context.getSources(), name) && context.depth != 0) {
  7.                         return null;
  8.                 }
  9.   // 根据target类型获取不同的Binder,可以是null(普通的类型一般是Null),MapBinder,CollectionBinder或ArrayBinder
  10.                 AggregateBinder<?> aggregateBinder = getAggregateBinder(target, context);
  11.   // 若aggregateBinder不为null,则调用bindAggregate并返回绑定后的对象
  12.                 if (aggregateBinder != null) {
  13.                         return bindAggregate(name, target, handler, context, aggregateBinder);
  14.                 }
  15.   // 若property不为null
  16.                 if (property != null) {
  17.                         try {
  18.         // 绑定属性到对象中,比如配置文件中设置了server.port=8888,那么将会最终调用bindProperty方法进行属性设置
  19.                                 return bindProperty(target, context, property);
  20.                         }
  21.                         catch (ConverterNotFoundException ex) {
  22.                                 // We might still be able to bind it using the recursive binders
  23.                                 Object instance = bindDataObject(name, target, handler, context, allowRecursiveBinding);
  24.                                 if (instance != null) {
  25.                                         return instance;
  26.                                 }
  27.                                 throw ex;
  28.                         }
  29.                 }
  30.   // 只有@ConfigurationProperties注解的类进行外部属性绑定才会走这里
  31.                 return bindDataObject(name, target, handler, context, allowRecursiveBinding);
  32.         }
  33. private Object bindDataObject(ConfigurationPropertyName name, Bindable<?> target, BindHandler handler,
  34.                         Context context, boolean allowRecursiveBinding) {
  35.                 if (isUnbindableBean(name, target, context)) {
  36.                         return null;
  37.                 }
  38.                 Class<?> type = target.getType().resolve(Object.class);
  39.                 if (!allowRecursiveBinding && context.isBindingDataObject(type)) {
  40.                         return null;
  41.                 }
  42.   // 新建一个DataObjectPropertyBinder的实现类对象,注意这个对象实现了bindProperty方法
  43.                 DataObjectPropertyBinder propertyBinder = (propertyName, propertyTarget) -> bind(name.append(propertyName),
  44.                                 propertyTarget, handler, context, false, false);
  45.                 return context.withDataObject(type, () -> {
  46.                         for (DataObjectBinder dataObjectBinder : this.dataObjectBinders) {
  47.         // 真正实现将外部配置属性绑定到@ConfigurationProperties注解的XxxProperties类的属性中的逻辑
  48.                                 Object instance = dataObjectBinder.bind(name, target, context, propertyBinder);
  49.                                 if (instance != null) {
  50.                                         return instance;
  51.                                 }
  52.                         }
  53.                         return null;
  54.                 });
  55.         }
复制代码
  https://zhhll.icu/2021/框架/springboot/源码/3.属性绑定原理/

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

王國慶

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表