@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初始化和注册机制,在本文中不再赘述。