目錄
3、@ConfigurationPropertiesScan
2)、AutoConfigurationImportSelector
按照SpringApplication的run方法執行流程,在refreshContext時候會調用refresh方法,其中會在ConfigurationClassPostProcessor中處理@Import,ImportSelector等(詳細可以參見:SpringIoc源碼(十)- ApplicationContext(六)- refresh(ConfigurationClassPostProcessor上)或Spring源碼-ImportSelector實現分析)。而自動裝配則是通過@SpringBootApplication註解實現,其註解中@ComponentScan或@Import(AutoConfigurationImportSelector.class)等都是ConfigurationClassPostProcessor進行處理的。所以按照Spring Boot啓動的執行時機,在這個地方開始分析自動裝配。
自動裝配就是@EnableAutoConfiguration註解,只是我們一般會直接使用@SpringBootApplication。Spring 要求是將該註解放在一個可以掃描的@Component上即可(之前分析過,解析@Component時,會將其上面的註解進行處理),所以我們一般會放到main方法啓動類上,前面(上一篇博客)分析過會將main方法啓動類進行註冊BeanDefinition。則會將其上面的註解進行解析和處理。
@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) })
@ConfigurationPropertiesScan
public @interface SpringBootApplication {
// 省略字段
}
1、@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration(proxyBeanMethods = false)
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
個人認爲之前是需要將@SpringBootApplication放在能註冊成@Component的類上,而現在自己本身就是一個@Component,再加上下面的@EnableAutoConfiguration本身會將該註解所在類的包進行ComponentScan處理(可以參見:SpringIoc源碼(十一)- ApplicationContext(七)- refresh(ConfigurationClassPostProcessor下))。
2、@ComponentScan
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
進行@ComponentScan處理,只是需要排除掉TypeExcludeFilter和AutoConfigurationExcludeFilter。
3、@ConfigurationPropertiesScan
@ConfigurationPropertiesScan是Spring Boot新增的註解,看字面意思是掃描所有的@ConfigurationProperties註解,並進行加載。具體看看註解的定義:
// 省去元註解
@Import(ConfigurationPropertiesScanRegistrar.class)
@EnableConfigurationProperties
public @interface ConfigurationPropertiesScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
具體實現過程參見:。
4、@EnableAutoConfiguration
// 省略元註解
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
通過註解我們可以配置不進行注入的名稱數組或者類數組,當讓默認情況下,@EnableAutoConfiguration被標記在@SpringBootApplication上使用,則該值都爲空。
再看@AutoConfigurationPackage,應該是將該文件類所在目錄的包下面的@Component註冊成Bean。@Import分析過會將AutoConfigurationImportSelector註冊成一個Bean。
1)、@AutoConfigurationPackage
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
通過@import將註冊成一個Bean,繼續看其結構:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
DeterminableImports是Spring Boot的接口,先不進行分析。之前分析過 ImportBeanDefinitionRegistrar 回調,會將BeanDefinitionRegistry(其實當前回傳的是AbstractApplicationContext)傳回來,進行進行BeanDefinition的注入,並且在refresh的最後階段將單利懶加載的Bean進行getBean操作全部初始化。回調時機發生在refresh的(詳細可以參考:SpringIoc源碼(十)- ApplicationContext(六)- refresh(ConfigurationClassPostProcessor上)):
AbstractApplicationContext # invokeBeanFactoryPostProcessors
PostProcessorRegistrationDelegate # invokeBeanFactoryPostProcessors
# invokeBeanDefinitionRegistryPostProcessors
ConfigurationClassPostProcessor # postProcessBeanDefinitionRegistry
# processConfigBeanDefinitions
ConfigurationClassBeanDefinitionReader # loadBeanDefinitions
# loadBeanDefinitionsForConfigurationClass
# loadBeanDefinitionsFromRegistrars
知道了執行時機,並且AutoConfigurationPackages.Registrar回調的時候會回傳對應的AnnotationMetadata(註解元數據)。
new PackageImport(metadata).getPackageName()
PackageImport(AnnotationMetadata metadata) {
this.packageName = ClassUtils.getPackageName(metadata.getClassName());
}
比較清楚,拿到@SpringBootApplication或@EnableAutoConfiguration註解所在類的包名,調用註冊方法:
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
// BEAN = AutoConfigurationPackages.class.getName();
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
}
else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
先判斷AutoConfigurationPackages本身是否有註冊成BeanDefinition(可能存在用戶自定義註冊等情況,考慮太全了)。如果是則在BeanDefinition上直接添加包信息,否則new 一個新的GenericBeanDefinition,再添加包信息。則會在refresh的最後階段依賴對所有單利非懶加載的Bean進行依賴注入。
2)、AutoConfigurationImportSelector
AutoConfigurationImportSelector 分爲兩部分進行分析,
首先是Spring對DeferredImportSelector的調用時機和解析處理過程(怎樣註冊成Bean的),參見:Spring源碼 - DeferredImportSelector實現分析。
第二部分是AutoConfigurationImportSelector的process和selectImports方法的處理過程,詳細參見:。