上一篇解析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时完成。
如果您觉得本文不错,欢迎关注我的微信公众号,您的关注是我坚持的动力!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。