一、入口
Springboot中@Configuration注解被掃描到的入口在AbstractApplicationContext類中的 invokeBeanFactoryPostProcessors(beanFactory) ; 方法中,方法上面有一行注解為 // Invoke factory processors registered as beans in the context.意思是把處理beanFactory的程式類型注冊為Context中的bean。
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
這句話的意思是spring-boot啟動添加不由容器管理的BeanFactoryPostProcessor,執行個體化後直接儲存在AbstractApplicationContext.beanFactoryPostProcessors。BeanFactoryPostProcessor包括以下 org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer$ ConfigurationWarningsPostProcessor org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$ CachingMetadataReaderFactoryPostProcessor org.springframework.boot.context.config.ConfigFileApplicationListener$ PropertySourceOrderingPostProcessor
2、BeanFactoryPostProcessors
在後面的invokeBeanFactoryPostProcessor()方法中,有兩個參數beanFactory和和beanFactoryPostProcessors。在beanFactoryPostProcessors中存在三個beanFactoryPostProcessor,分别是 1、ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor 這個類的作用是檢視@ComponentScan掃描的包有沒有問題,有的話則進行列印,在Spring-boot中這個類基本使用不到了,因為在@ SpringBootApplication注解中包含了 @ComponentScan (、 @SpringBootConfiguration、 @SpringBootConfiguration注解,是以這個類就不起作用了 2、 SharedMetadataReaderFactoryContextInitializer$ CachingMetadataReaderFactoryPostProcessor 3、 ConfigFileApplicationListener$ PropertySourceOrderingPostProcessor
然後去beanFactory中取出一個 BeanDefinitionRegistryPostProcessor類型的beanConfigurationClassPostProcessor,與beanfactory一起進入了方法 invokeBeanDefinitionRegistryPostProcessors (currentRegistryProcessors , registry) ;
3、ConfigurationClassPostProcessor
上次放我們說 invokeBeanDefinitionRegistryPostProcessors (currentRegistryProcessors , registry) ;方法中傳入了兩個參數一個是ConfigurationClassPostProcessor,一個是BeanFactory,那麼我們看下ConfigurationClassPostProcessor類,解析@Configuration注解的入口方法:processConfigBeanDefinitions,入參是beanFactory
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
首先是擷取beanFactory中加載的所有BeanDefinition:String[] candidateNames = registry.getBeanDefinitionNames(); 然後周遊操作,将我們項目啟動的入口bean,Applicaition類放入到 configCandidates集合中。 最後一Application類作為參數傳入到 ConfigurationClassParser類的parse()方法中。
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
然後在ConfigurationClassParser類中的 componentScanParser的parse方法,對@Component注解的**/*.class,即目前Application同級目錄下的類,進行掃描
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
4、@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
上面的注解是在SpringBootApplication中的注解,意思是Spring掃描的包中排除的類,其中 TypeExcludeFilter.class是排除特定類型的bean加入, AutoConfigurationExcludeFilter.class意思是,如果你自己聲明了一個DubboConfiguration然後也用pom檔案中配置了spring-dubbo-start啟用了帶有@AutoConfiguration注解的bean,那麼會忽略你自己寫的bean,但前提是,兩個@Configuration的類的名稱要一模一樣,否則不起作用
到這裡隻是說了Spring中怎麼排除的bean,還沒有講到在什麼地方加載的@Configuration注解,後面會接着開始說@Configuration'注解的bean是什麼時間被加載的