用法:https://blog.csdn.net/qq_32868023/article/details/123390627
原理一:https://blog.csdn.net/qq_32868023/article/details/123515652
原理二:https://blog.csdn.net/qq_32868023/article/details/123603241
原理三:https://blog.csdn.net/qq_32868023/article/details/123604439
原理四:https://blog.csdn.net/qq_32868023/article/details/123606236
原理五:https://blog.csdn.net/qq_32868023/article/details/123616110
本节主要介绍@EnableConfigurationProperties和@ConfigurationPropertiesScan注解是如何将bean注册到beanFactory的,以及JAVA_BEAN和VALUE_OBJECT两种绑定方式
1、@EnableConfigurationProperties
@EnableConfigurationProperties通过Import EnableConfigurationPropertiesRegistrar这个类来注册Bean,EnableConfigurationPropertiesRegistrar是一个ImportBeanDefinitionRegistrar,看一下registerBeanDefinitions
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerInfrastructureBeans(registry);
registerMethodValidationExcludeFilter(registry);
ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
getTypes(metadata).forEach(beanRegistrar::register);
}
这里面做了两个事情
1、registerInfrastructureBeans注册了一些基础设施
static void registerInfrastructureBeans(BeanDefinitionRegistry registry) {
ConfigurationPropertiesBindingPostProcessor.register(registry);
BoundConfigurationProperties.register(registry);
}
其实就是注册了ConfigurationPropertiesBindingPostProcessor和BoundConfigurationProperties,ConfigurationPropertiesBindingPostProcessor会在bean实例化阶段进行属性绑定;BoundConfigurationProperties作用后面介绍
为什么@Bean+@Component方式时,beanFactory里也会存在ConfigurationPropertiesBindingPostProcessor?
因为有ConfigurationPropertiesAutoConfiguration自动配置类,通过@EnableConfigurationProperties注册了ConfigurationPropertiesBindingPostProcessor
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
public class ConfigurationPropertiesAutoConfiguration {
}
2、new了一个ConfigurationPropertiesBeanRegistrar,将@EnableConfigurationProperties指定的类注册到beanFactory,看一下bean注册过程
final class ConfigurationPropertiesBeanRegistrar {
void register(Class<?> type) {
MergedAnnotation<ConfigurationProperties> annotation = MergedAnnotations
.from(type, SearchStrategy.TYPE_HIERARCHY).get(ConfigurationProperties.class);
register(type, annotation);
}
void register(Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {
String name = getName(type, annotation);
if (!containsBeanDefinition(name)) {
registerBeanDefinition(name, type, annotation);
}
}
private void registerBeanDefinition(String beanName, Class<?> type,
MergedAnnotation<ConfigurationProperties> annotation) {
Assert.state(annotation.isPresent(), () -> "No " + ConfigurationProperties.class.getSimpleName()
+ " annotation found on '" + type.getName() + "'.");
this.registry.registerBeanDefinition(beanName, createBeanDefinition(beanName, type));
}
}
其实就是创建了BeanDefinition,注册到BeanFactory里,beanName逻辑就是prefix-类全限定名,如下
private String getName(Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {
String prefix = annotation.isPresent() ? annotation.getString("prefix") : "";
return (StringUtils.hasText(prefix) ? prefix + "-" + type.getName() : type.getName());
}
BeanDefinition的创建逻辑,如果bindMethod == BindMethod.VALUE_OBJECT,提供一个InstanceSupplier,这里面调用了Binder#bindOrCreate来绑定bean的属性,这种情况下ConfigurationPropertiesBindingPostProcessor将不会处理这个bean
private BeanDefinition createBeanDefinition(String beanName, Class<?> type) {
BindMethod bindMethod = BindMethod.forType(type);
RootBeanDefinition definition = new RootBeanDefinition(type);
definition.setAttribute(BindMethod.class.getName(), bindMethod);
if (bindMethod == BindMethod.VALUE_OBJECT) {
definition.setInstanceSupplier(() -> createValueObject(beanName, type));
}
return definition;
}
private Object createValueObject(String beanName, Class<?> beanType) {
ConfigurationPropertiesBean bean = ConfigurationPropertiesBean.forValueObject(beanType, beanName);
ConfigurationPropertiesBinder binder = ConfigurationPropertiesBinder.get(this.beanFactory);
try {
return binder.bindOrCreate(bean);
}
catch (Exception ex) {
throw new ConfigurationPropertiesBindException(bean, ex);
}
}
BindMethod是什么呢?
public enum BindMethod {
/**
* Java Bean using getter/setter binding.
*/
JAVA_BEAN,
/**
* Value object using constructor binding.
*/
VALUE_OBJECT;
static BindMethod forType(Class<?> type) {
return (ConfigurationPropertiesBindConstructorProvider.INSTANCE.getBindConstructor(type, false) != null)
? VALUE_OBJECT : JAVA_BEAN;
}
}
BindMethod是个枚举,JAVA_BEAN表示用getter、setter来绑定值,VALUE_OBJECT表示用构造器绑定,判断一个类是JAVA_BEAN还是VALUE_BEAN就看有没有@ConstructorBinding注解
2、@ConfigurationPropertiesScan
@ConfigurationPropertiesScan Import了ConfigurationPropertiesScanRegistrar,是ImportBeanDefinitionRegistrar接口的实现。它的逻辑也很简单,就是拿到要扫描的包名,通过ClassPathScanningCandidateComponentProvider这个Scanner去扫描带有ConfigurationProperties的类,然后通过ConfigurationPropertiesBeanRegistrar将扫描到的bean注册到beanFactory。逻辑跟@ComponentScan差不多,不继续往下看了
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
scan(registry, packagesToScan);
}