SpringBoot之@ConfigurationProperties自動注入成員變數值功能原始碼解析
前言:
我們在使用SpringBoot開發相關專案時,經常會使用到@ConfigurationProperties註解,這個註解配合application.properties(或yml檔案)使用,可以快速的為我們的Bean的成員變數進行賦值,常規用法如下:
// 建立bean @ConfigurationProperties(prefix="person") @Data public class Person { private int id; private String name; private String addr; } // 在application.properties檔案中新增 person.name=jack person.id=11 person.addr=china
這樣就可以為Person例項自動注入屬性
那麼SpringBoot是如何為bean例項注入屬性值的呢?
1.關於自動注入屬性值的功能猜想
SpringBoot會為每一個在配置檔案中有匹配項的bean注入屬性值,根據之前的 BeanPostProcessor功能可知,我們的Spring容器中應該有一個Bean實現BeanPostProcessor介面,然後在返回每一個bean之前,獲取配置檔案內容,找到對應屬性,然後進行賦值操作
SpringBoot中確實有這麼一個Bean,ConfigurationPropertiesBindingPostProcessor,實現了相關賦值功能,那麼這個Bean是如何被注入到容器中的呢,下面親隨筆者一層層分析查詢
2.SpringBoot專案啟動之@SpringBootApplication
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {
重要的註解就是@EnableAutoConfiguration,下面看下這個註解
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
主要功能就是把EnableAutoConfigurationImportSelector注入到容器中,下面看下這個類
public class EnableAutoConfigurationImportSelector
extends AutoConfigurationImportSelector {
@Override
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass().equals(EnableAutoConfigurationImportSelector.class)) {
return getEnvironment().getProperty(
EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,
true);
}
return true;
}
}
// AutoConfigurationImportSelector
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {}
AutoConfigurationImportSelector這個類實現了DeferredImportSelector介面,可知這個類應該在其selectImport方法中,應該獲取了一個bean的列表,然後被這個bean列表的所有bean會被注入到容器中,下面重點分析下這個selectImport方法
3.AutoConfigurationImportSelector.selectImport(AnnotationMetadata annotationMetadata)
該方法原始碼如下:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
// 1.獲取META-INF/spring-autoconfigure-metadata.properties 路徑下所有bean URL
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 2.重要操作在這裡
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
// 3.經過中間的一系列操作返回configurations
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
// getCandidateConfigurations(annotationMetadata,attributes)
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
// 主要就是為了獲取META-INF/spring.factories 檔案下EnableAutoConfiguration對應的value值,並返回該值
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
通過以上分析可知:AutoConfigurationImportSelector類的主要功能就是為了載入META-INF/spring.factories檔案下EnableAutoConfiguration對應的value值,這些value值對應一個個bean,Spring會將這些bean載入到容器中
我們看下是哪些bean(在spring-boot-autoconfigure-xxx.jar的META-INF/spring.factories中)
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
...
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
...
org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\
...
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
以上展示了部分類,可知,這些類都會被注入到Spring中,下面我們重點關注ConfigurationPropertiesAutoConfiguration類
4.ConfigurationPropertiesAutoConfiguration
1)ConfigurationPropertiesAutoConfiguration原始碼如下:
@Configuration
@EnableConfigurationProperties
public class ConfigurationPropertiesAutoConfiguration {
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EnableConfigurationPropertiesImportSelector.class})
public @interface EnableConfigurationProperties {
Class<?>[] value() default {};
}
該類沒有其他方法,由其註解@EnableConfigurationProperties可知,整個類主要是為了注入EnableConfigurationPropertiesImportSelector這個類,
2)EnableConfigurationPropertiesImportSelector原始碼如下:
class EnableConfigurationPropertiesImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata metadata) {
MultiValueMap attributes = metadata.getAllAnnotationAttributes(EnableConfigurationProperties.class.getName(),
false);
Object[] type = attributes == null ? null : (Object[]) ((Object[]) attributes.getFirst("value"));
return type != null && type.length != 0
? new String[]{ConfigurationPropertiesBeanRegistrar.class.getName(),
ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName()}
: new String[]{ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName()};
}
}
可知其會返回ConfigurationPropertiesBindingPostProcessorRegistrar類到容器中
3)ConfigurationPropertiesBindingPostProcessorRegistrar原始碼如下
public class ConfigurationPropertiesBindingPostProcessorRegistrar implements ImportBeanDefinitionRegistrar {
public static final String BINDER_BEAN_NAME = ConfigurationPropertiesBindingPostProcessor.class.getName();
private static final String METADATA_BEAN_NAME;
// 主要的注入方法
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(BINDER_BEAN_NAME)) {
BeanDefinitionBuilder meta = BeanDefinitionBuilder
.genericBeanDefinition(ConfigurationBeanFactoryMetaData.class);
BeanDefinitionBuilder bean = BeanDefinitionBuilder
.genericBeanDefinition(ConfigurationPropertiesBindingPostProcessor.class);
bean.addPropertyReference("beanMetaDataStore", METADATA_BEAN_NAME);
// 真正的注入功能就是這句話,將ConfigurationPropertiesBindingPostProcessor類註冊進來
registry.registerBeanDefinition(BINDER_BEAN_NAME, bean.getBeanDefinition());
registry.registerBeanDefinition(METADATA_BEAN_NAME, meta.getBeanDefinition());
}
}
static {
METADATA_BEAN_NAME = BINDER_BEAN_NAME + ".store";
}
}
總結:通過以上的一層層分析,我們終於到了ConfigurationPropertiesBindingPostProcessor類,也成功將該類注入到容器中,下面我們就分析下ConfigurationPropertiesBindingPostProcessor是如何將引數注入到bean中