SpringBoot源码解析 -- @ComponentScan的实现原理

3,146 阅读3分钟

SpringBoot深入理解 -- @AliasFor注解的作用
SpringBoot源码解析 -- SpringBoot启动过程
SpringBoot源码解析 -- AutoConfigure的实现原理
SpringBoot源码解析 -- @ComponentScan的实现原理
SpringBoot源码解析 -- @Value,@Autowired实现原理
SpringBoot源码解析 -- Tomcat,SpringMVC启动
SpringBoot源码解析 -- Logging,Environment启动

上一篇解析SpringBoot AutoConfigure功能的文章说过,ConfigurationClassParser#doProcessConfigurationClass方法很重要,处理@Component,@PropertySources,@ComponentScans,@Import,@ImportResource等注解。 现在来看一下@ComponentScans注解的处理。
源码分析基于spring boot 2.1

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,如果有,递归处理

ComponentScanAnnotationParser#parse -> ClassPathBeanDefinitionScanner#doScan

	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#scanCandidateComponents

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 保存结果

ClassPathScanningCandidateComponentProvider#isCandidateComponent

	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筛选BeanDefinition

ClassPathScanningCandidateComponentProvider#registerDefaultFilters方法,会给includeFilter添加默认的AnnotationTypeFilter,负责处理@Component,@ManagedBean等注解。

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时完成。

如果您觉得本文不错,欢迎关注我的微信公众号,您的关注是我坚持的动力!