• Spring实例化源码解析之ClassPathBeanDefinitionScanner(五)


    Spring实例化源码解析之ClassPathBeanDefinitionScanner(五)

    上一章我们分析了ComponentScanAnnotationParser,主要就是分析了@ComponentScan注解内的属性和属性的作用,而注解解析的信息会交给ClassPathBeanDefinitionScanner扫描器使用,也就是本章需要分析的内容。话不多说,直接进入正题。

    doScan(String… basePackages)

    首先入口就是doScan方法,我们按照规矩还是从注释开始。

    在指定的基础包中执行扫描,并返回注册的 Bean 定义。该方法不会注册注解配置处理器,而是将这个责任留给调用者。

    解释如下:

    1. “Perform a scan within the specified base packages, returning the registered bean definitions.”:在指定的基础包中执行扫描,即扫描这些包及其子包中的类文件,以查找与 Spring 相关的注解(例如 @Component@Service@Repository 等)标记的类,这些都是上一章节的内容。扫描的结果将会得到注册为 Bean 的定义

    2. “This method does not register an annotation config processor”:这个方法不会注册注解配置处理器。注解配置处理器是负责解析和处理注解配置的组件,例如 @Configuration@Bean@ComponentScan 等。通常,在 Spring 中会有一个注解配置处理器负责处理这些注解,并将它们转换为相应的 Bean 定义。但是,这个方法并不包含这个处理器的注册过程。@Configuration@Bean第二章也有分析和介绍。

    3. “but rather leaves this up to the caller.”:相反,这个方法将这个责任留给调用者。也就是说,调用者需要自己处理注解配置,包括注册注解配置处理器和执行相应的解析和处理过程。

    总结起来,这段话的意思是,该方法提供了执行扫描并返回注册的 Bean 定义的功能,但不包含具体的注解配置处理过程,这个过程需要由调用者自行处理。调用者需要负责注册注解配置处理器,并执行相应的解析和处理操作,以确保扫描到的注解配置能够正确地转换为相应的 Bean 定义。

    /**
    	 * Perform a scan within the specified base packages,
    	 * returning the registered bean definitions.
    	 * 

    This method does not register an annotation config processor * but rather leaves this up to the caller. * @param basePackages the packages to check for annotated classes * @return set of beans registered if any for tooling registration purposes (never {@code null}) */ protected Set<BeanDefinitionHolder> doScan(String... basePackages) { // 这里面的代码看起来很简单,但是他应该是处理了@ComponentScan注解里的所有属性 Assert.notEmpty(basePackages, "At least one base package must be specified"); // 存储所有扫描并注册好的beanDefinitions,set集合 Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); // 我加的,用于输出注释,源码中忽略。 AtomicInteger index = new AtomicInteger(); for (String basePackage : basePackages) { // 包含过滤器的使用在此 // 根据名称 查询候选的Components,根据包路径,返回的是Bean定义信息集合,所以核心在此。 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) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // 在这里就把我们需要spring管理的类组装BeanDefinition,放到了(BeanDefinitionRegistry)BeanFactory中 System.out.println("当前加载的beanName:"+beanName); index.addAndGet(1); registerBeanDefinition(definitionHolder, this.registry); } } } System.out.println("当前加载的所有beanName的个数为:"+ index); return beanDefinitions; }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    根据方法的源码来看,for循环中的findCandidateComponents方法就是其核心方法。打端点可以看到candidates返回的就是我们自定义的这些bean的定义信息。
    在这里插入图片描述

    findCandidateComponents

    查询候选的Component修饰的class,从方法名称就能判断出其干了什么,spring源码中的方法定义的还是非常优秀。basePackage来源于我的AopConfig类中的注解。默认情况下componentsIndex为null,也就是说默认会走else逻辑。

    public Set<BeanDefinition> findCandidateComponents(String basePackage) {
        // 这个basePackage来源于注解类AopConfig
        // componentsIndex不知道是什么,应该是一个扩展点,因为spring启动在这里打断点,此处为null
        if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
           return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
        }
        else {
           // 默认走这里
           return scanCandidateComponents(basePackage);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    scanCandidateComponents

    scanCandidateComponents方法就是去扫描,根据basePackage去组装包的搜索路径,也就是我们想要被spring管理的bean的路径,然后根据路径去获取resources。

    private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
        Set<BeanDefinition> candidates = new LinkedHashSet<>();
        try {
           // 根据basePackage组装包的搜索路径,当前是classpath*:com.qhyu.cloud.**/**/*.class
           String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                 resolveBasePackage(basePackage) + '/' + this.resourcePattern;
           // 获取资源数据,就是具体到文件
           Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
           // 所以我们平常的日志需要打开trace和debug日志,至少在开发过程中需要这么做
           boolean traceEnabled = logger.isTraceEnabled();
           boolean debugEnabled = logger.isDebugEnabled();
           for (Resource resource : resources) {
              if (traceEnabled) {
                 logger.trace("Scanning " + resource);
              }
              try {
                 // 通过resource获取到元数据
                 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                 // 通过元数据判断是不是候选component,所以此处是核心
                 // 此处用到了排除过滤器和包含过滤器
                 if (isCandidateComponent(metadataReader)) {
                    ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                    sbd.setSource(resource);
                    if (isCandidateComponent(sbd)) {
                       if (debugEnabled) {
                          logger.debug("Identified candidate component class: " + resource);
                       }
                       candidates.add(sbd);
                    }
                    else {
                       if (debugEnabled) {
                          logger.debug("Ignored because not a concrete top-level class: " + resource);
                       }
                    }
                 }
                 else {
                    if (traceEnabled) {
                       logger.trace("Ignored because not matching any filter: " + resource);
                    }
                 }
              }
              catch (FileNotFoundException ex) {
                 if (traceEnabled) {
                    logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());
                 }
              }
              catch (Throwable ex) {
                 throw new BeanDefinitionStoreException(
                       "Failed to read candidate component class: " + resource, ex);
              }
           }
        }
        catch (IOException ex) {
           throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
        }
        return candidates;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    根据basePackage组装的包搜索路径,获取到文件资源。

    Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath)

    在这里插入图片描述

    拿到所有的resources之后循环判断是不是候选的Component类,如果是就执行candidates.add。

    isCandidateComponent

    这里就使用到了排除过滤器和包含过滤器。作用是根据给定的 MetadataReader 对象,结合排除过滤器和包含过滤器,判断该组件是否符合候选组件的条件。过滤器可以用于排除或包含特定的组件类型或特征,以实现组件的筛选和过滤。

    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
        for (TypeFilter tf : this.excludeFilters) {
           if (tf.match(metadataReader, getMetadataReaderFactory())) {
              return false;
           }
        }
        for (TypeFilter tf : this.includeFilters) {
           if (tf.match(metadataReader, getMetadataReaderFactory())) {
              return isConditionMatch(metadataReader);
           }
        }
        return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    如果和excludeFilters有匹配就直接返回false,并且至少与一个includeFilters匹配,同时需要满足没有@Condition注解。

    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        AnnotationMetadata metadata = beanDefinition.getMetadata();
        return (metadata.isIndependent() && (metadata.isConcrete() ||
              (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    判断注解元数据是否符合候选组件的条件,返回布尔值。

    • metadata.isIndependent(): 判断注解元数据表示的类是否是独立的,即不依赖于其他类。如果是独立的,则继续下面的判断条件。
    • metadata.isConcrete(): 判断注解元数据表示的类是否是具体的类,即非抽象类。如果是具体类,则返回 true。
    • (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName())): 判断注解元数据表示的类是否是抽象类,并且是否具有被 @Lookup 注解标注的方法。如果是抽象类且具有 @Lookup 注解的方法,则返回 true。

    registerBeanDefinition

    拿到了bean的定义信息之后,就往工厂DefaultListableBeanFactory中注册,也就是put进beanDefinitionMap中。

    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) {
    					// lazyinit在这里,getBean之后才用
    					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
    				}
    				if (candidate instanceof AnnotatedBeanDefinition) {
    					// 设置beanDefinition的属性信息,后续用
    					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
    				}
    				if (checkCandidate(beanName, candidate)) {
    					// 组装holder
    					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
    					definitionHolder =
    							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    					beanDefinitions.add(definitionHolder);
    					// 在这里就把我们需要spring管理的类组装BeanDefinition,放到了(BeanDefinitionRegistry)BeanFactory中
    					System.out.println("当前加载的beanName:"+beanName);
    					index.addAndGet(1);
    					//DefaultListableBeanFactory中的beanDefinitionMap中
    					registerBeanDefinition(definitionHolder, this.registry);
    				}
    			}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    这段代码是一个循环遍历,对一组候选的 BeanDefinition 进行处理和注册。让我们逐行进行分析:

    1. ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
      解析候选的 BeanDefinition 的作用域(scope)元数据。
    2. candidate.setScope(scopeMetadata.getScopeName());
      设置候选的 BeanDefinition 的作用域为解析得到的作用域名称。
    3. String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
      使用 BeanNameGenerator 生成候选 BeanDefinition 的唯一名称。
    4. if (candidate instanceof AbstractBeanDefinition) { ... }
      如果候选的 BeanDefinition 是 AbstractBeanDefinition 的实例,执行下面的代码块。
      • postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
        对 AbstractBeanDefinition 进行后处理,例如设置懒加载等属性。
    5. if (candidate instanceof AnnotatedBeanDefinition) { ... }
      如果候选的 BeanDefinition 是 AnnotatedBeanDefinition 的实例,执行下面的代码块。
      • AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
        处理通用的注解定义,例如处理 @Lazy@Primary@DependsOn 等注解。
    6. if (checkCandidate(beanName, candidate)) { ... }
      检查候选 BeanDefinition 是否符合要求,即是否满足注册的条件。
    7. BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
      创建一个 BeanDefinitionHolder 对象,用于持有候选 BeanDefinition 和其对应的名称。
    8. definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
      根据作用域元数据,应用相应的代理模式(如创建作用域代理)。
    9. beanDefinitions.add(definitionHolder);
      将 BeanDefinitionHolder 添加到 beanDefinitions 集合中,用于后续的处理。
    10. registerBeanDefinition(definitionHolder, this.registry);
      将 BeanDefinition 注册到 BeanDefinitionRegistry 中,即将其添加到 BeanFactory 的 beanDefinitionMap 中,以便后续的获取和使用。

    该代码片段的作用是将一组候选的 BeanDefinition 进行处理和注册,将它们转化为完整的 BeanDefinition,并添加到 BeanFactory 中以便后续的实例化和管理。在处理过程中,会解析作用域元数据、生成唯一的 Bean 名称、处理注解定义、应用代理模式等操作,以确保注册的 BeanDefinition 符合预期的配置和行为。

    总结

    至此,我们所有需要被spring管理的bean的定义信息都被注册到工厂中,后续bean的初始化实例化都是后续的工作了。invokeBeanFactoryPostProcessors(beanFactory)这句代码就分析了五章,后续将开始BeanFactoryPostProcessors的注册源码分析。

  • 相关阅读:
    入坑计算机视觉必备的图像基础
    Mybatis 框架 ( 四 ) QueryWrapper
    C++ 基础与深度分析 Chapter8 动态内存管理(动态内存基础、智能指针、相关问题)
    Java#32(异常, File)
    分析 20 个 veToken 生态系统协议 这种代币模型为何受欢迎?
    网络简答题带答案
    freertos之临界区管理
    Python数据结构(队列)
    Leetode-891-子序列宽度之和
    南京大学:新时代数字化人才培养方案探讨
  • 原文地址:https://blog.csdn.net/Tanganling/article/details/133354486