-
先谈`@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的,要获取到有两种方式,
-
在 TestConfigurationProperties
上添加注解@Component
-
使用 @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