@EnableConfigurationProperties和@ConfigurationProperties的区别
在springboot的开发中我们经常会使用到@ConfigurationProperties赖获取配置的值绑定到项目中的bean中,要完成这个功能核心的注解是@ConfigurationProperties。但是在项目中经常看到另外一个注解@EnableConfigurationProperties也是为了实现类似的功能,那么它们之间有什么关系呢?
区别
@EnableConfigurationProperties的作用有两个:
1、导入EnableConfigurationPropertiesRegistrarc.class这个类,这个类会向容器中注册ConfigurationPropertiesBindingPostProcessor处理器
2、该注解有value属性,需要填入class对象。只要填入的class被标记了@ConfigurationProperties注解,那么该class无需注册为spring的Bean,它默认会创建这个Bean并绑定配置值注册到容器中。
@ConfigurationProperties的作用:
配合ConfigurationPropertiesBindingPostProcessor这个BeanPostProcessor完成配置值绑定到目标Bean。
使用配置绑定到Bean这个功能需要先开启@EnableConfigurationProperties才能使@ConfigurationProperties生效。但是实际项目中我们确实可以不用手动开启@EnableConfigurationProperties就能使用@ConfigurationProperties,原因是springboot的自动配置很多都用到了@EnableConfigurationProperties这个注解,一旦这个注解被触发,那我们在项目中就可以直接使用而不需要开启。
@EnableConfigurationProperties
@EnableConfigurationProperties注解导入了EnableConfigurationPropertiesRegistrar.class,该class实现了ImportBeanDefinitionRegistrar类,会向spring容器注册核心类ConfigurationPropertiesBindingPostProcessor,这个class实现了spring容器周期的BeanPostProcessor接口,在周期方法postProcessBeforeInitialization中会解析每个bean,如果某个bean被标记了@ConfigurationProperties注解,那么会把配置值绑定到此bean中。
package org.springframework.boot.context.properties;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EnableConfigurationPropertiesRegistrar.class})
public @interface EnableConfigurationProperties {
String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
Class<?>[] value() default {};
}
@EnableConfigurationProperties 注解
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(2);
registry.registerBeanDefinition(BEAN_NAME, definition);
}
ConfigurationPropertiesBinder.register(registry);
}
导入类EnableConfigurationPropertiesRegistrar会向容器注入ConfigurationPropertiesBindingPostProcessor处理器,这里的BEAN_NAME就是ConfigurationPropertiesBindingPostProcessor类的全限定名。ConfigurationPropertiesBinder.register(registry);向容器中注册ConfigurationPropertiesBinder,该对象是真正的绑定逻辑执行者,会从容器的environment对象中获取值绑定到目标Bean中
/**
* EnableConfigurationPropertiesRegistrar类注册代码
* registerInfrastructureBeans(registry); 注册ConfigurationPropertiesBindingPostProcessor
*/
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerInfrastructureBeans(registry);
registerMethodValidationExcludeFilter(registry);
ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
this.getTypes(metadata).forEach(beanRegistrar::register);
}
private Set<Class<?>> getTypes(AnnotationMetadata metadata) {
return (Set)metadata.getAnnotations().stream(EnableConfigurationProperties.class).flatMap((annotation) -> {
return Arrays.stream(annotation.getClassArray("value"));
}).filter((type) -> {
return Void.TYPE != type;
}).collect(Collectors.toSet());
}
this.getTypes(metadata).forEach(beanRegistrar::register);如果@EnableConfigurationProperties 注解的value属性(数组Class)有值,那么将继续探测类(数组中的Class)上是否有@ConfigurationProperties,如果有,那么注册这个类到容器中,后续创建Bean时配合ConfigurationPropertiesBindingPostProcessor绑定值。
@ConfigurationProperties
BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
Bindable<?> target = propertiesBean.asBindTarget();
ConfigurationProperties annotation = propertiesBean.getAnnotation();
BindHandler bindHandler = this.getBindHandler(target, annotation);
return this.getBinder().bind(annotation.prefix(), target, bindHandler);
}
执行的是ConfigurationPropertiesBindingPostProcessor中的绑定方法