Spring @ConfigurationProperties源码详解(2)

@EnableConfigurationProperties源码解析
是时候来看看 @EnableConfigurationProperties ()的源码了:
@Target (ElementType. TYPE )
@Retention (RetentionPolicy. RUNTIME )
@Documented
@Import (EnableConfigurationPropertiesImportSelector. class ) // ***重点***
public @ interface EnableConfigurationProperties {
//注册 ConfigurationProperties bean快捷点,也可以注册普通bean
Class<?>[] value() default {};
}

我们看到注解有一个@Import元注解。 @Import我们之前介绍过,会优先将属性指定的类注册到应用上下文中。我们来看看 EnableConfigurationPropertiesImportSelector这个类。
class EnableConfigurationPropertiesImportSelector implements ImportSelector {...}
这是一个非public的类,我们看到这个类实现了 ImportSelector接口,接口如下:
public interface ImportSelector {
//根据导入类的元数据信息,返回需要import的满足条件的类名
//导入类指的是标记@Import注解的类,这里指的是 @EnableConfigurationProperties
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
我们看到有了这个导入选择机制,我们就可以在 @EnableConfigurationProperties 中根据元数据信息,动态确定Import那些Bean了。 @EnableConfigurationProperties具体的业务代码如下:
@Override
public String[] selectImports(AnnotationMetadata metadata) {
//获取EnableConfigurationProperties的所有属性列表
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
EnableConfigurationProperties . class .getName(), false );
//获取value属性的配置属性类对象列表,例如如下示例中的AppConfigProperties.class类对象
//@EnableConfigurationProperties(AppConfigProperties.class)
Object[] type = attributes == null ? null
: (Object[]) attributes.getFirst( "value" );
//如果没有设置value,仅返回ConfigurationPropertiesBindingPostProcessorRegistrar.class
if (type == null || type. length == 0 ) {
return new String[] {
ConfigurationPropertiesBindingPostProcessorRegistrar. class
.getName() };
}
//如果有value,补上ConfigurationPropertiesBeanRegistrar. class
return new String[] { ConfigurationPropertiesBeanRegistrar. class .getName(),
ConfigurationPropertiesBindingPostProcessorRegistrar. class .getName() };
}

至此,我们来改改我们的 EnableSwitch 例子:
//@EnableConfigurationProperties()
@Import (ConfigurationPropertiesBindingPostProcessorRegistrar. class )
public class EnableSwitch {
}
发现也可以正确绑定配置属性。

先来看一下没有value的情况,这时候仅仅是一个 @EnableConfigurationProperties开关,不注册配置属性类。我们猜一下, ConfigurationPropertiesBindingPostProcessorRegistrar. class很有可能是实现配置文件数据绑定的地方。
public class ConfigurationPropertiesBindingPostProcessorRegistrar
implements ImportBeanDefinitionRegistrar {
//***找到啦***,真正执行配置属性绑定的处理器ConfigurationPropertiesBindingPostProcessor
public static final String BINDER_BEAN_NAME = ConfigurationPropertiesBindingPostProcessor. class .getName();
private static final String METADATA_BEAN_NAME = BINDER_BEAN_NAME + ".store" ;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
//如果从未注册过ConfigurationPropertiesBindingPostProcessor
if (!registry.containsBeanDefinition( BINDER_BEAN_NAME )) {
//元数据类bean定义,工具类,在beanfactory初始化中存储的元数据
BeanDefinitionBuilder meta = BeanDefinitionBuilder
. genericBeanDefinition (ConfigurationBeanFactoryMetaData. class );
//配置属性绑定处理器bean定义
BeanDefinitionBuilder bean = BeanDefinitionBuilder. genericBeanDefinition (
ConfigurationPropertiesBindingPostProcessor. class );
bean.addPropertyReference( "beanMetaDataStore" , METADATA_BEAN_NAME );
//注册上
registry.registerBeanDefinition( BINDER_BEAN_NAME , bean.getBeanDefinition());
registry.registerBeanDefinition( METADATA_BEAN_NAME , meta.getBeanDefinition());
}
}
}

配置属性绑定处理器
ConfigurationPropertiesBindingPostProcessor就是我们苦苦寻找的配置属性绑定处理器 了。我们来看看它的源码:
public class ConfigurationPropertiesBindingPostProcessor
implements BeanPostProcessor, BeanFactoryAware, ResourceLoaderAware,
EnvironmentAware, ApplicationContextAware, InitializingBean, DisposableBean,
ApplicationListener<ContextRefreshedEvent>, PriorityOrdered {
...
//默认优先级很高,尽早被容器注册初始化
private int order = Ordered. HIGHEST_PRECEDENCE + 1 ;
...
private void postProcessBeforeInitialization (Object bean, String beanName,
ConfigurationProperties annotation) {
Object target = bean;//配置类
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
target);
//如果制定了locations,则从locations加载配置文件并封装成PropertySources
if (annotation != null && annotation.locations(). length != 0 ) {
factory.setPropertySources(
loadPropertySources(annotation.locations(), annotation.merge()));
}
//默认属性来源,加载了environment
else {
factory.setPropertySources( this . propertySources );
}
//验证器
factory.setValidator(determineValidator(bean));
//转换器
factory.setConversionService( this . conversionService == null
? getDefaultConversionService() : this . conversionService );
if (annotation != null ) {
factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
factory.setExceptionIfInvalid(annotation.exceptionIfInvalid());
factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties());
//前缀
if (StringUtils. hasLength (annotation.prefix())) {
factory.setTargetName(annotation.prefix());
}
}
try {
//处理配置来源和配置属性bean的绑定
factory.bindPropertiesToTarget();
}
catch (Exception ex) {
String targetClass = ClassUtils. getShortName (target.getClass());
throw new BeanCreationException(beanName, "Could not bind properties to "
+ targetClass + " (" + getAnnotationDetails(annotation) + ")" , ex);
}
}
...
}
ConfigurationPropertiesBeanRegistrar
最后我们来看看@ConfigurationProperties有value属性的时候,会初始化这个Bean:

public static class ConfigurationPropertiesBeanRegistrar
implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
MultiValueMap<String, Object> attributes = metadata
.getAllAnnotationAttributes(
EnableConfigurationProperties . class .getName(), false );
//收集所有value中的类对象
List<Class<?>> types = collectClasses(attributes.get( "value" ));
for (Class<?> type : types) {
//ConfigurationProperties注解指定的前缀
String prefix = extractPrefix(type);
String name = (StringUtils. hasText (prefix) ? prefix + "-" + type.getName()
: type.getName());
if (!registry.containsBeanDefinition(name)) {
//注册ConfigurationProperties类到容器中
registerBeanDefinition(registry, type, name);
}
}
}
...
private void registerBeanDefinition(BeanDefinitionRegistry registry,
Class<?> type, String name) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
. genericBeanDefinition (type);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
//Bean注册
registry.registerBeanDefinition(name, beanDefinition);
ConfigurationProperties properties = AnnotationUtils. findAnnotation (type,
ConfigurationProperties . class );
Assert. notNull (properties,
"No " + ConfigurationProperties . class .getSimpleName()
+ " annotation found on '" + type.getName() + "'." );
}
...
}

至此我们了解了@ConfigurationProperties自动绑定属性数据到配置属性类的全过程。关于 bean注册和bean处理器等细节涉及到spring bean factory初始化和注册机制,在本文中不再赘述。

相关代码见

猜你喜欢

转载自blog.csdn.net/ws008/article/details/79994787