Spring基于注解的扫描底层流程

调用流程(基于调试记录下来的)

   1.refresh方法下的invokeBeanFactoryPostProcessors(beanFactory);
  2.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
  3.invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);【ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry方法】
  4.processConfigBeanDefinitions(registry);
  5.parser.parse(candidates);【parser是ConfigurationClassParser类对@ComponentScan @Configuration支持】
  6.processConfigurationClass(new ConfigurationClass(metadata, beanName));
  7.sourceClass = doProcessConfigurationClass(configClass, sourceClass);
  8.Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
  9.scanner.doScan(StringUtils.toStringArray(basePackages));【scanner指ComponentScanAnnotationParser类】
  10.Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
  11.scanCandidateComponents(basePackage);

核心方法分析

  doProcessConfigurationClass方法分析

protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException {

    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        // Recursively process any member (nested) classes first
        // 处理内部类
        // 在解析一个配置类时,如果类上有@Component,则会判断内部类是不是lite配置类并进行解析,并且会记录为被导入的
        processMemberClasses(configClass, sourceClass, filter);
    }

    // Process any @PropertySource annotations
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
        else {
            logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }

    // Process any @ComponentScan annotations
    // 会进行扫描,得到的BeanDefinition会注册到Spring容器中,并且会检查是不是配置类并进行解析
    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
            // 这里就会进行扫描,得到的BeanDefinition会注册到Spring容器中
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            // 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();
                }
                // 检查扫描出来的BeanDefinition是不是配置类(full和lite)
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                    parse(bdCand.getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }

    // Process any @Import annotations
    // getImports(sourceClass)会拿到@Import导入的类
    // 如果导入的是普通类,那么会直接把它当做配置类来解析
    // 如果导入的是普通ImportSelector,那么会将返回的类再次调用processImports()
    // 如果导入的是特殊ImportSelector,DeferredImportSelector,那么暂时不会处理,会在解析完所有当前这轮配置类后进行导入,将返回的类再次调用processImports()
    // 如果导入的是ImportBeanDefinitionRegistrar,那么暂时不会处理,会在解析完所有当前这轮配置类后,将配置类解析成为BeanDefinition之后进行调用
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

    // Process any @ImportResource annotations
    AnnotationAttributes importResource =
            AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {
        String[] resources = importResource.getStringArray("locations");
        Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
        for (String resource : resources) {
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }

    // Process individual @Bean methods
    // 解析配置类中的@Bean,但并没有真正处理@Bean,只是暂时找出来
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // Process default methods on interfaces
    // 解析配置类所实现的接口中的@Bean,但并没有真正处理@Bean,只是暂时找出来
    processInterfaces(configClass, sourceClass);

    // Process superclass, if any
    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (superclass != null && !superclass.startsWith("java") &&
                !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            // Superclass found, return its annotation metadata and recurse
            return sourceClass.getSuperClass();
        }
    }

    // No superclass -> processing is complete
    return null;
}

 

 

 

  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);
        for (Resource resource : resources) {
            if (resource.isReadable()) {
                try {
                    //包装了类的基本信息的对象
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    //如果类上面有includeFilters注解
                    if (isCandidateComponent(metadataReader)) {
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setResource(resource);
                        sbd.setSource(resource);
                        if (isCandidateComponent(sbd)) {
                            candidates.add(sbd);
                        }
                    }
                }
                catch (Throwable ex) {}
            }
        }
    }
    catch (IOException ex) {}
    return candidates;
}

    汇总说明

        1. 首先,通过ResourcePatternResolver获得指定包路径下的所有 .class 文件(Spring源码中将此文件包装成了Resource对象
        2. 遍历每个Resource对象
        3. 利用MetadataReaderFactory解析Resource对象得到MetadataReader(在Spring源码中MetadataReaderFactory具体的实现类为CachingMetadataReaderFactory,MetadataReader的具体实现类为SimpleMetadataReader)
        4. 利用MetadataReader进行excludeFilters和includeFilters,以及条件注解@Conditional的筛选(条件注解并不能理解:某个类上是否存在@Conditional注解,如果存在则调用注解中所指定的类的match方法进行匹配,匹配成功则通过筛选,匹配失败则pass掉。)
        5. 筛选通过后,基于metadataReader生成ScannedGenericBeanDefinition
        6. 再基于metadataReader判断是不是对应的类是不是接口或抽象类【能作为Bean的不能是抽象的或者是接口,需要是独立的,能产生对象的】
        7. 如果筛选通过,那么就表示扫描到了一个Bean,将ScannedGenericBeanDefinition加入结果集

 

    额外部分说明

      MetadataReader类分析

        MetadataReader表示类的元数据读取器,主要包含了一个AnnotationMetadata,功能有

            1. 获取类的名字、
            2. 获取父类的名字
            3. 获取所实现的所有接口名
            4. 获取所有内部类的名字
            5. 判断是不是抽象类
            6. 判断是不是接口
            7. 判断是不是一个注解
            8. 获取拥有某个注解的方法集合
            9. 获取类上添加的所有注解信息
            10. 获取类上添加的所有注解类型集合

 

        值得注意的是,CachingMetadataReaderFactory解析某个.class文件得到MetadataReader对象是利用ASM技术,并没有加载这个类到JVM。并且,最终得到的ScannedGenericBeanDefinition对象,beanClass属性存储的是当前类的名字,而不是class对象。(beanClass属性的类型是Object,它即可以存储类的名字,也可以存储class对象【这也就是为什么下一步会有加载类这个步骤】

 

  doScan方法分析

    代码展示(删减了部分日志记录与异常抛出部分)

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        //扫描到有注解的类并封装成BeanDefinition对象
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        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);
            }
            if (candidate instanceof AnnotatedBeanDefinition) {
                //支持了@Lazy @DependOn注解
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                //这里不重要
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);

                //BeanDefinition注册
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

 

    汇总说明

      1.基于findCandidateComponents方法获得的ScannedGenericBeanDefinition的Set集合

      2.通过扫描BeanDefinition的Metadata属性获取元数据上的信息,包括一些注解进行额外处理,如@Lazy @DependOn注解

      3.然后把处理过的BeanDefinition注册到容器当中

 

    额外部分说明

      ComponentScanAnnotationParser类作为扫描器怎么具备注册功能?

        它中的属性 registry(private final BeanDefinitionRegistry registry;【BeanDefinitionRegistry虽然是接口,基于多态的性质,填充的具体实现类是DefaultListableBeanFactory类,也就是我们常说的Spring容器的具体对象】

          这个属性对象具备了注册的功能:

            它会调用 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());  注册BeanDefinition到容器。

            它会调用 registry.registerAlias(beanName, alias); 注册BeanDefinition的别名。

 

        BeanDefinitionHolder的作用与意义(个人理解)

        BeanDefinition本身并不会存储 beanName与aliases(别名),它所存储的是我们扫描出来的某个类对象元数据的一些信息,而且它也是一个接口,具体的实现类很多,采用 BeanDefinitionHolder 更像是一种装饰器模式的思维,用于存储BeanDefinition本身信息之外,还存储一些额外信息。

 

        重复扫描会不会带来问题?

        是不会的,因为  if (checkCandidate(beanName, candidate))  这一步存在判断。

protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
    return (!(existingDefinition instanceof ScannedGenericBeanDefinition) ||  // explicitly registered overriding bean
            (newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) ||  // scanned same file twice
            newDefinition.equals(existingDefinition));  // scanned equivalent class twice
}

          判断来源是否一致,是否和已存在的兼容。如果是的话,返回false,其实就会避开再次注册。

 

doProcessConfigurationClass
posted @ 2022-09-08 04:26  忧愁的chafry  阅读(456)  评论(0编辑  收藏  举报