SpringBoot @EnableConfigurationPropertie 注解作用源码分析


  • 先谈`@ConfigurationProperties`  注解

  • @EnableConfigurationProperties 注解使用示例

  • 源码分析

    • `EnableConfigurationProperties` 源码

    • @ConfigurationProperties 源码


先谈@ConfigurationProperties  注解

@ConfigurationProperties 注解主要用于将配置文件properties或 yml 中的属性转换到Bean中的属性

比如像下面这样

properties.name=test
@Getter
@Setter
@ConfigurationProperties(prefix = "properties")
public class TestConfigurationProperties {
    private String name;

}

但是这样我们是在Spring容器中是获取不到TestConfigurationProperties 这个bean的,要获取到有两种方式,

  1. TestConfigurationProperties 上添加注解@Component
  2. 使用@EnableConfigurationProperties 注解

@EnableConfigurationProperties 注解使用示例

@Configuration
@EnableConfigurationProperties(TestConfigurationProperties.class)
public class TestAutoConfiguration 
{

    private TestConfigurationProperties testConfigurationProperties;

    public TestAutoConfiguration(TestConfigurationProperties testConfigurationProperties) {
        this.testConfigurationProperties = testConfigurationProperties;
    }

    @Bean
    public User user(){
        User user = new User();
        user.setName(testConfigurationProperties.getName());
        return user;
    }
}

两种注入的方式没有本质区别,都是注入到spring容器中

源码分析

spring boot 版本 2.3.2.RELEASE

EnableConfigurationProperties 源码

我们看看EnableConfigurationProperties 的源码

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

 /**
  * The bean name of the configuration properties validator.
  * @since 2.2.0
  */

 String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";

 /**
  * Convenient way to quickly register
  * {@link ConfigurationProperties @ConfigurationProperties} annotated beans with
  * Spring. Standard Spring Beans will also be scanned regardless of this value.
  * @return {@code @ConfigurationProperties} annotated beans to register
  */

 Class<?>[] value() default {};

}

这里可以看到导入了配置类EnableConfigurationPropertiesRegistrar,很明显核心的实现在EnableConfigurationPropertiesRegistrar 类中,所以我们看看EnableConfigurationPropertiesRegistrar的源码

class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {

 @Override
 public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
  registerInfrastructureBeans(registry);
  ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
    //获取 `@EnableConfigurationProperties` 注解指定的 Class 类对象,并注册对应的 BeanDefinition
  getTypes(metadata).forEach(beanRegistrar::register);
 }

 private 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.register(registry);
  BoundConfigurationProperties.register(registry);
  ConfigurationBeanFactoryMetadata.register(registry);
 }

}

可以看到EnableConfigurationProperties 实现类ImportBeanDefinitionRegistrar 接口,然后在方法registerBeanDefinitions中获取到注解中的Bean然后注册到spring容器中

@ConfigurationProperties 源码

@ConfigurationProperties 注解源码很简单

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigurationProperties {

 /**
  * The prefix of the properties that are valid to bind to this object. Synonym for
  * {@link #prefix()}. A valid prefix is defined by one or more words separated with
  * dots (e.g. {@code "acme.system.feature"}).
  * @return the prefix of the properties to bind
  */

 @AliasFor("prefix")
 String value() default "";

 /**
  * The prefix of the properties that are valid to bind to this object. Synonym for
  * {@link #value()}. A valid prefix is defined by one or more words separated with
  * dots (e.g. {@code "acme.system.feature"}).
  * @return the prefix of the properties to bind
  */

 @AliasFor("value")
 String prefix() default "";

 /**
  * Flag to indicate that when binding to this object invalid fields should be ignored.
  * Invalid means invalid according to the binder that is used, and usually this means
  * fields of the wrong type (or that cannot be coerced into the correct type).
  * @return the flag value (default false)
  */

 boolean ignoreInvalidFields() default false;

 /**
  * Flag to indicate that when binding to this object unknown fields should be ignored.
  * An unknown field could be a sign of a mistake in the Properties.
  * @return the flag value (default true)
  */

 boolean ignoreUnknownFields() default true;

}

没有导入任何配置类,让“@ConfigurationProperties 起作用的主要是类`ConfigurationPropertiesBindingPostProcessor

核心方法主要是  bind方法和属性ConfigurationPropertiesBinder

// 属性绑定器
private ConfigurationPropertiesBinder binder;

private void bind(ConfigurationPropertiesBean bean) {
   // 如果这个 bean 为空,或者已经处理过,则直接返回
  if (bean == null || hasBoundValueObject(bean.getName())) {
   return;
  }
  // 对 @ConstructorBinding 的校验,如果使用该注解但是没有找到合适的构造器,那么在这里抛出异常
  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 {
      // 通过 Binder 将指定 prefix 前缀的属性值设置到这个 Bean 中
   this.binder.bind(bean);
  }
  catch (Exception ex) {
   throw new ConfigurationPropertiesBindException(bean, ex);
  }
 }

可以看到核心方法是

  this.binder.bind(bean);

  BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
  Bindable<?> target = propertiesBean.asBindTarget();
  ConfigurationProperties annotation = propertiesBean.getAnnotation();
  // 获取一个 BindHandler 绑定处理器
  BindHandler bindHandler = getBindHandler(target, annotation);
  // 通过这个 首先获取Binder对象,然后将指定 prefix 前缀的属性值设置到这个 Bean 中
  return getBinder().bind(annotation.prefix(), target, bindHandler);
 }

整个bean绑定过程还是比较复杂的,这里就不作完整展开了,感兴趣可以自己从这里给出的源码入口去具体分析


原文始发于微信公众号(小奏技术):SpringBoot @EnableConfigurationPropertie 注解作用源码分析

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/30199.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!