上一篇解析SpringBoot AutoConfigure功能的文章說過,ConfigurationClassParser#doProcessConfigurationClass方法很重要,處理@Component,@PropertySources,@ComponentScans,@Import,@ImportResource等註解。
如今來看一下@ComponentScans註解的處理。
源碼分析基於spring boot 2.1spring
ConfigurationClassParser#doProcessConfigurationClass微信
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { ... for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { // #1 if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } ... } Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); // #2 if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { // #3 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()); // #4 // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { // #5 parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } ... }
#1
處理@PropertySources註解,獲取對應的PropertySources屬性源,添加到Environment中
關於PropertySources與Environment的關係,後面會寫文章解析。#2
獲取SourceClass上的ComponentScans配置#3
若是存在@Conditional註解,取註解中Condition條件判斷類進行判斷#4
使用ComponentScanAnnotationParser處理ComponentScan,掃描指定目錄下的bean#5
檢查掃描出來的bean是否還有ConfigurationClass,若是有,遞歸處理app
ComponentScanAnnotationParser#parse -> ClassPathBeanDefinitionScanner#doScanide
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { Set<BeanDefinition> candidates = findCandidateComponents(basePackage); // #1 for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); // #2 } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); // #3 } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this.registry); // #4 } } } return beanDefinitions; }
#1
掃描路徑,獲取候選類#2
給bean設置默認的配置,如LazyInit,AutowireMode,InitMethodName#3
從Class獲取註解配置信息(如@Lazy,@DependsOn),設置到BeanDefinition, #4
將掃描到的BeanDefinition註冊到spring中源碼分析
ClassPathBeanDefinitionScanner#findCandidateComponents -> ClassPathScanningCandidateComponentProvider#scanCandidateComponentspost
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); // #1 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); // #2 if (isCandidateComponent(metadataReader)) { // #3 ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); // #4 sbd.setResource(resource); sbd.setSource(resource); if (isCandidateComponent(sbd)) { // #5 if (debugEnabled) { logger.debug("Identified candidate component class: " + resource); } candidates.add(sbd); // #6 } ... }
#1
掃描給定目錄及子目錄下全部的class文件#2
生成SimpleMetadataReader,使用ASM讀取class文件#3
判斷掃描到的BeanDefinition是否知足注入條件#4
生成ScannedGenericBeanDefinition,該BeanDefinition實現了AnnotatedBeanDefinition接口,使用ASM(複用SimpleMetadataReader)獲取Class的註解信息,而不須要JVM加載class
AnnotatedBeanDefinition對BeanDefinition擴展,能夠獲取Class的註解信息。
AnnotationMetadata表示Class註解的元數據,標準實現類爲StandardAnnotationMetadata,而AnnotationMetadataReadingVisitor使用訪問者模式,經過ASM獲取註解信息。#5
檢查BeanDefinition是否爲非接口,非循環依賴#6
保存結果this
ClassPathScanningCandidateComponentProvider#isCandidateComponentlua
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { // #1 return false; } } for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { // #2 return isConditionMatch(metadataReader); } } return false; }
#1
使用excludeFilters過濾BeanDefinition#2
使用includeFilters篩選BeanDefinitionspa
ClassPathScanningCandidateComponentProvider#registerDefaultFilters方法,會給includeFilter添加默認的AnnotationTypeFilter,負責處理@Component,@ManagedBean等註解。debug
AnnotationTypeFilter#match -> matchSelf
protected boolean matchSelf(MetadataReader metadataReader) { AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); // #1 return metadata.hasAnnotation(this.annotationType.getName()) || // #2 (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName())); // #3 }
#1
獲取Class的註解元數據#2
檢查Class上是否有對應的annotationType#3
檢查Class的嵌套註解是否有對應的annotationType
@Service,@Repository,@Controller註解上都標註了@Component註解,若是Class上使用了這些註解,#3
步驟是返回true的
到這裏,@ComponentScans註解掃描標註了@Component的Bea的實現原理就說完了。
簡單來講,Spring掃描對應目錄下的class,生成BeanDefinition並註冊到Spring上下文。最後構造bean的操做,是在AbstractApplicationContext#refresh方法中,調用finishBeanFactoryInitialization,構建熱加載的單例bean時完成。
若是您以爲本文不錯,歡迎關注個人微信公衆號,您的關注是我堅持的動力!