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

上一篇解析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 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 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 doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set beanDefinitions = new LinkedHashSet<>();
        for (String basePackage : basePackages) {
            Set 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 scanCandidateComponents(String basePackage) {
    Set 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时完成。

如果您觉得本文不错,欢迎关注我的微信公众号,您的关注是我坚持的动力!
SpringBoot源码解析 -- @ComponentScan的实现原理_第1张图片

你可能感兴趣的:(spring,springboot,源码分析)