1 概述
2 實(shí)例
3 配置注入實(shí)現(xiàn)原理
1 概述
使用過(guò)Spring Boot的都會(huì)知道臭埋,我們可以在application.properties文件中進(jìn)行一系列的配置踪央,該配置會(huì)被自動(dòng)注入到我們需要使用的bean中,下面我們就介紹配置注入的實(shí)現(xiàn)原理瓢阴。
首先畅蹂,要知道在application.properties中的配置是通過(guò)BeanPostProcessor
進(jìn)行注入的,具體完成該功能的BeanPostProcessor
實(shí)現(xiàn)類是ConfigurationPropertiesBindingPostProcessor
荣恐。
本文接下來(lái)會(huì)介紹ConfigurationPropertiesBindingPostProcessor
是在何時(shí)被加入到beanfactory中的液斜,以及@EnableConfigurationProperties
、@ConfigurationProperties
注解的實(shí)現(xiàn)原理叠穆。
2 實(shí)例
Spring Boot自動(dòng)配置中充斥著大量使用通過(guò)application.properties進(jìn)行擴(kuò)展配置的實(shí)現(xiàn)少漆,比如我們熟悉的MybatisAutoConfiguration
:
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
//使用MybatisProperties類獲得application.properties中的實(shí)現(xiàn)
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {
...
}
下面我們?cè)诳聪?code>MybatisProperties類的定義:
//指定擴(kuò)展配置在application.properties中的配置前綴為mybatis
@ConfigurationProperties(
prefix = "mybatis"
)
public class MybatisProperties {
public static final String MYBATIS_PREFIX = "mybatis";
...
}
通過(guò)如上的注解,MyBatis就可以獲得我們?cè)赼pplication.properties中的配置了痹束,比如如下配置:
mybatis.config-location=classpath:configurations/mybatis/mybatis-config.xml
3 配置注入實(shí)現(xiàn)原理
要了解配置注入的實(shí)現(xiàn)原理检疫,首先要找到上文介紹到的ConfigurationPropertiesBindingPostProcessor
是何時(shí)被注冊(cè)到beanfactory中的,通過(guò)閱讀源碼發(fā)現(xiàn)祷嘶,在spring.factories文件中有如下一行:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
...
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
...
可見(jiàn)屎媳,是通過(guò)@EnableAutoConfiguration
注解引入了相關(guān)的配置夺溢,@EnableAutoConfiguration
通過(guò)@Import
注解自動(dòng)配置的原理這里不再介紹。我們看下ConfigurationPropertiesAutoConfiguration
類的實(shí)現(xiàn):
//兩個(gè)重要的注解Configuration使得該類被視為配置類進(jìn)行處理
//EnableConfigurationProperties則實(shí)現(xiàn)了配置注入
@Configuration
@EnableConfigurationProperties
public class ConfigurationPropertiesAutoConfiguration {
}
首先ConfigurationPropertiesAutoConfiguration
被@Configuration
注解烛谊,因此會(huì)在beanfactory加載時(shí)被作為配置類處理风响,具體在ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry
調(diào)用ConfigurationClassParser.parse
進(jìn)行處理。
我們?cè)倏?code>@EnableConfigurationProperties定義:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesImportSelector.class)
public @interface EnableConfigurationProperties {
/**
* Convenient way to quickly register {@link ConfigurationProperties} annotated beans
* with Spring. Standard Spring Beans will also be scanned regardless of this value.
* @return {@link ConfigurationProperties} annotated beans to register
*/
Class<?>[] value() default {};
}
@EnableConfigurationProperties
通過(guò)@Import(EnableConfigurationPropertiesImportSelector.class)
向容器中注入了相關(guān)的處理類丹禀,@Import
也是在ConfigurationClassParser.parse
進(jìn)行處理的状勤。
EnableConfigurationPropertiesImportSelector
類定義如下:
class EnableConfigurationPropertiesImportSelector implements ImportSelector {
private static final String[] IMPORTS = { ConfigurationPropertiesBeanRegistrar.class.getName(),
ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() };
//該類主要通過(guò)@Import實(shí)現(xiàn)機(jī)制向beanfactory進(jìn)行了配置注入相關(guān)類的注冊(cè)
//其中ConfigurationPropertiesBeanRegistrar負(fù)責(zé)找到所有EnableConfigurationProperties
//注解中聲明的配置類,并將其注冊(cè)到beanfactory中双泪,如上面例子中的
//@EnableConfigurationProperties({MybatisProperties.class}),
//MybatisProperties將被注入到beanfactory中
//ConfigurationPropertiesBindingPostProcessorRegistrar則負(fù)責(zé)
//在實(shí)例化類時(shí)持搜,檢測(cè)該類是否被ConfigurationProperties注解,如果是的話
//則從application.properties獲取相應(yīng)前綴的配置焙矛,然后進(jìn)行配置注入
@Override
public String[] selectImports(AnnotationMetadata metadata) {
return IMPORTS;
}
/**
* {@link ImportBeanDefinitionRegistrar} for configuration properties support.
*/
//ConfigurationPropertiesBeanRegistrar是EnableConfigurationPropertiesImportSelector的內(nèi)部類
public static class ConfigurationPropertiesBeanRegistrar implements ImportBeanDefinitionRegistrar {
//向beanfactory中注入所有注解EnableConfigurationProperties.value中指定的配置類
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
getTypes(metadata).forEach((type) -> register(registry, (ConfigurableListableBeanFactory) registry, type));
}
//獲取該配置類EnableConfigurationProperties.value中指定的配置類
private List<Class<?>> getTypes(AnnotationMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata
.getAllAnnotationAttributes(EnableConfigurationProperties.class.getName(), false);
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());
}
private void register(BeanDefinitionRegistry registry, ConfigurableListableBeanFactory beanFactory,
Class<?> type) {
String name = getName(type);
if (!containsBeanDefinition(beanFactory, name)) {
registerBeanDefinition(registry, name, type);
}
}
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());
}
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;
}
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() + "'.");
}
}
}
好了葫盼,到這里,我們已經(jīng)知道注解@EnableConfigurationProperties.value
中指定的配置類是如何被注入到beanfactory中的了村斟,那么注解EnableConfigurationProperties.value
中指定的配置類中的屬性是如何從application.properties被注入的呢贫导?比如上面的
//指定擴(kuò)展配置在application.properties中的配置前綴為mybatis
@ConfigurationProperties(
prefix = "mybatis"
)
public class MybatisProperties {
public static final String MYBATIS_PREFIX = "mybatis";
...
}
對(duì)應(yīng)配置如下:
mybatis.config-location=classpath:configurations/mybatis/mybatis-config.xml
這就是我們前面提到的BeanPostProcessor
接口實(shí)現(xiàn)類ConfigurationPropertiesBindingPostProcessor
類處理的,而ConfigurationPropertiesBindingPostProcessor
則是通過(guò)上面EnableConfigurationPropertiesImportSelector
中使用類ConfigurationPropertiesBindingPostProcessorRegistrar
進(jìn)行注冊(cè)的:
public class ConfigurationPropertiesBindingPostProcessorRegistrar implements ImportBeanDefinitionRegistrar {
//通過(guò)如下方法注入了ConfigurationPropertiesBindingPostProcessor
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(ConfigurationPropertiesBindingPostProcessor.BEAN_NAME)) {
registerConfigurationPropertiesBindingPostProcessor(registry);
registerConfigurationBeanFactoryMetadata(registry);
}
}
private void registerConfigurationPropertiesBindingPostProcessor(BeanDefinitionRegistry registry) {
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(ConfigurationPropertiesBindingPostProcessor.class);
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(ConfigurationPropertiesBindingPostProcessor.BEAN_NAME, definition);
}
private void registerConfigurationBeanFactoryMetadata(BeanDefinitionRegistry registry) {
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(ConfigurationBeanFactoryMetadata.class);
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(ConfigurationBeanFactoryMetadata.BEAN_NAME, definition);
}
}
ConfigurationPropertiesBindingPostProcessor
是接口BeanPostProcessor
的實(shí)現(xiàn)蟆盹,會(huì)被ApplicationContext
檢測(cè)出來(lái)孩灯,在實(shí)例化每個(gè)bean時(shí)會(huì)被調(diào)用進(jìn)行擴(kuò)展處理。
//ConfigurationPropertiesBindingPostProcessor
//類ConfigurationPropertiesBindingPostProcessor在方法postProcessBeforeInitialization
//進(jìn)行配置注入逾滥,在下面的源碼中可以看到我們熟悉的注解@ConfigurationProperties
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
ConfigurationProperties annotation = getAnnotation(bean, beanName, ConfigurationProperties.class);
if (annotation != null) {
bind(bean, beanName, annotation);
}
return bean;
}