概述
EnableConfigurationPropertiesImportSelector是Spring Boot提供的一个ImportSelector实现,其目的是绑定外部属性到使用了注解@ConfigurationProperties的属性配置bean上。但具体的属性绑定的动作并不是由该ImportSelector来完成的,而是由它所指定的两个ImportBeanDefinitionRegistrar所注册到容器的bean来完成的 :
ConfigurationPropertiesBeanRegistrar该
ImportBeanDefinitionRegistrar是EnableConfigurationPropertiesImportSelector定义的一个内部类,它用于获取配置类上注解@EnableConfigurationProperties所指定要注册为bean的属性配置类,并将它们作为bean注册到容器。ConfigurationPropertiesBindingPostProcessorRegistrarEnableConfigurationPropertiesImportSelector通过ConfigurationPropertiesBindingPostProcessorRegistrar注册一个ConfigurationPropertiesBindingPostProcessor到容器,而ConfigurationPropertiesBindingPostProcessor的任务则是绑定外部属性到使用了注解@ConfigurationProperties的属性配置bean上。
关于应用
EnableConfigurationPropertiesImportSelector会被注解@EnableConfigurationProperties导入,以确保属性绑定所需的ConfigurationPropertiesBeanRegistrar和ConfigurationPropertiesBindingPostProcessor两个工作组件提前就绪:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesImportSelector.class)
public @interface EnableConfigurationProperties {
Class<?>[] value() default {};
}
源代码
源代码版本 : ``
package org.springframework.boot.context.properties;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
class EnableConfigurationPropertiesImportSelector implements ImportSelector {
private static final String[] IMPORTS = {
ConfigurationPropertiesBeanRegistrar.class.getName(),
ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() };
@Override
public String[] selectImports(AnnotationMetadata metadata) {
return IMPORTS;
}
/**
* ImportBeanDefinitionRegistrar for configuration properties support.
* 内部类,一个 ImportBeanDefinitionRegistrar 实现,获取配置类上
* 注解 @EnableConfigurationProperties 所
* 指定要注册为bean的属性配置类,并将它们作为bean注册到容器
*/
public static class ConfigurationPropertiesBeanRegistrar
implements ImportBeanDefinitionRegistrar {
// 获取注解元数据 metadata 所属类上注解 @EnableConfigurationProperties 所
// 指定要注册为bean的属性配置类,并将它们作为bean注册到容器
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
getTypes(metadata).forEach((type) -> register(registry,
(ConfigurableListableBeanFactory) registry, type));
}
// 获取注解元数据 metadata 所属类上注解 @EnableConfigurationProperties 的元数据,
// 分析出其中要注册的目标 bean 的类,这里的目标 bean 通常是一些配置属性 bean ,也就是
// 使用了注解 @ConfigurationProperties 的配置属性 bean
private List<Class<?>> getTypes(AnnotationMetadata metadata) {
// 获取注解 @EnableConfigurationProperties 的所有属性,记录到
// attributes,每个属性可能有多个值,所以使用数据结构 MultiValueMap
MultiValueMap<String, Object> attributes = metadata
.getAllAnnotationAttributes(
EnableConfigurationProperties.class.getName(), false);
// 从 @EnableConfigurationProperties 注解的 value 属性中分析出类,
// 这些事将要注册为 bean 的属性bean的类
return collectClasses((attributes != null) ? attributes.get("value")
: Collections.emptyList());
}
private List<Class<?>> collectClasses(List<?> values) {
return values.stream().flatMap((value) -> Arrays.stream((Object[]) value))
.map((o) -> (Class<?>) o).filter((type) -> void.class != type)
.collect(Collectors.toList());
}
// 根据目标bean类型确定目标bean的名称,如果同名bean尚未注册,则注册该bean
private void register(BeanDefinitionRegistry registry,
ConfigurableListableBeanFactory beanFactory, Class<?> type) {
// 根据目标bean类型,和注解@ConfigurationProperties中指定的前缀信息
// 构建目标bean的名称
String name = getName(type);
if (!containsBeanDefinition(beanFactory, name)) {
// 如果名称为 name 的 bean 定义不存在于容器中,则注册之
registerBeanDefinition(registry, name, type);
}
}
// 根据目标bean类型上的注解 @ConfigurationProperties 元数据中配置属性项前缀信息
// 和目标bean类型构建目标bean的名称:
// 1. 如果注解 @ConfigurationProperties 被使用并且指定了前缀 , 返回 : 前缀-类名
// 2. 否则返回 : 类名
private String getName(Class<?> type) {
ConfigurationProperties annotation = AnnotationUtils.findAnnotation(type,
ConfigurationProperties.class);
String prefix = (annotation != null) ? annotation.prefix() : "";
return (StringUtils.hasText(prefix) ? prefix + "-" + type.getName()
: type.getName());
}
// 如果容器中已经包含指定名称的bean则返回true,否则返回false
private boolean containsBeanDefinition(
ConfigurableListableBeanFactory beanFactory, String name) {
if (beanFactory.containsBeanDefinition(name)) {
return true;
}
BeanFactory parent = beanFactory.getParentBeanFactory();
if (parent instanceof ConfigurableListableBeanFactory) {
return containsBeanDefinition((ConfigurableListableBeanFactory) parent,
name);
}
return false;
}
// 使用指定的名称name注册bean
private void registerBeanDefinition(BeanDefinitionRegistry registry, String name,
Class<?> type) {
assertHasAnnotation(type);
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(type);
registry.registerBeanDefinition(name, definition);
}
private void assertHasAnnotation(Class<?> type) {
Assert.notNull(
AnnotationUtils.findAnnotation(type, ConfigurationProperties.class),
() -> "No " + ConfigurationProperties.class.getSimpleName()
+ " annotation found on '" + type.getName() + "'.");
}
}
}
博客主要介绍了一个实现,其目的是将外部属性绑定到使用注解的属性配置上。具体绑定动作由注册到容器的组件完成,还提到该实现会被注解导入以确保工作组件就绪,同时给出了源代码版本及相关文章。
4665

被折叠的 条评论
为什么被折叠?



