Spring @ConfigurationProperties源码详解(2)

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

@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, PriorityOrdered {

//默认优先级很高,尽早被容器注册初始化

private int
order
= Ordered.
HIGHEST_PRECEDENCE
+
1
;

private void
postProcessBeforeInitialization
(Object bean, String beanName,

ConfigurationProperties
annotation) {
Object target = bean;//配置类
PropertiesConfigurationFactory factory =
new
PropertiesConfigurationFactory(
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初始化和注册机制,在本文中不再赘述。

相关代码见
https://github.com/zhaolin81/tutorial/tree/master/spring/src/main/java/com/zhaolin81/spring/framework/configproperties/annotation


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring @ConfigurationProperties源码详解(2)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

© 2014 - 2020 Java 技术驿站   网站地图  | 

icp 湘ICP备14000180

>>> 网站已平稳运行:

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏