(Spring)Bean生命周期(一)--- 扫包源码

197 阅读3分钟

Spring生命周期图解

springBean生命周期.png

Bean在启动前期会先扫描包路径然后生成BeanDefintion,先看看下Spring扫描的底层实现(扫描流程图见文章最下方):

Spring源码ClassPathBeanDefintionScanner#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);
      省略
      .................................
}

首先是迭代basePackages(扫包路径com.xxx) 进入到第一个方法 findCandidateComponents

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
   if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
      return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
   }
   else {
      return scanCandidateComponents(basePackage);
   }
}

这个方法有两个分支 A分支代表了指定了Bean B分支是我们平时用的扫描模式,在这里我们也只看B分支 ClassPathScanningCandidateComponentProvider#scanCandidateComponents

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
   Set<BeanDefinition> candidates = new LinkedHashSet<>();
   try {
      // 获取basePackage下所有的文件资源
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
      Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
      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);
               // excludeFilters、includeFilters判断
               if (isCandidateComponent(metadataReader)) { // @Component-->includeFilters判断
                  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 (Throwable ex) {
               throw new BeanDefinitionStoreException(
                     "Failed to read candidate component class: " + resource, ex);
            }
         }
         else {
            if (traceEnabled) {
               logger.trace("Ignored because not readable: " + resource);
            }
         }
      }
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException("I/O failure during classpath scanning", 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判断是不是对应的类是不是接口或抽象类
  7. 如果筛选通过,那么就表示扫描到了一个Bean,将ScannedGenericBeanDefinition加入结果集 简单来讲就是扫描指定包下类,找到符合规则的(带有@Component注解并且对应的类不是接口和抽象类没有被过滤排除,在有条件注解的情况下满足条件)转换成BeanDefintion加入候选BeanDefintion集合
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) {
       //刚刚拿到的候选BeanDefintion
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);

      for (BeanDefinition candidate : candidates) {
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         candidate.setScope(scopeMetadata.getScopeName());
         //获取BeanName
         String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

         if (candidate instanceof AbstractBeanDefinition) {
            //设置BeanDefinition的默认值
            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
         }
         if (candidate instanceof AnnotatedBeanDefinition) {
            // 解析@Lazy、@Primary、@DependsOn、@Role、@Description
            AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
         }

         // 检查Spring容器中是否已经存在该beanName
         if (checkCandidate(beanName, candidate)) {
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            definitionHolder =
                  AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);

            // 注册
            registerBeanDefinition(definitionHolder, this.registry);
         }
      }
   }
   return beanDefinitions;
}
  1. 遍历刚刚的候选BeanDefintion。
  2. 获取BeanDefintion元数据同时设置默认的Scope为单例。
  3. 获取BeanName 这里要看注解是否指定了beanName,指定则使用指定的beanName,不指定则使用当前类目小写首字母作为BeanName。
  4. 设置BeanDefinition的默认值。
  5. 解析BeanDefinition对应类上是否存在@Lazy、@Primary、@DependsOn、@Role、@Description 注解,存在则标记到BeanDefinition中去。
  6. 检查Spring容器中是否已经存在该beanName 这里要看是否兼容,如果不兼容则抛出异常说明BeanName已存在,兼容的情况是在有多个扫包路径扫到同一个类的情况下。
  7. 生成BeanDefinitionHolder存入集合中,BeanDefinitionHolder顾名思义就是对BeanDefinition的持有,同时持有的包括BeanDefinition的名称和别名。
  8. 注册,要判断当前BeanName是否在Spring容器中存在,如果不存在则把BeanName以及BeanDefinition注册到map中,如果存在则报错。

扫包流程图

spring扫包流程.png