1. 程式人生 > >SpringBoot之@ConfigurationProperties自動注入成員變數值功能原始碼解析

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中