@ComponentScan源码分析

本文详细解析了Spring框架中@ComponentScan注解的处理流程,从ConfigurationClassParser类的doProcessConfigurationClass方法入手,深入探讨了组件扫描的具体实现,包括如何获取BeanDefinition信息、扫描路径的确定及过滤器的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前有过一篇文章,介绍@Enable编程模型,其中提到在org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass方法里面有对配置类上面的@ComponentScan处理的逻辑,文章里面对@ComponentScan的处理逻辑简单带过,着重奖了对@Import的注解处理逻辑,这里将详细描述@ComponentScan注解的处理过程。

简介

@ComponentScan是用来定义spring程序启动时扫描的包路径,对于路径下面被@Component或者“继承”@Component注解的注解标注的类,将会进行Beandefintion信息加载。

看过之前一篇文章介绍,应该可以知道@ComponentScan注解跟@Import注解一样,标注在被@Configuration注解标注的类上面,处理的时机跟@Import基本一致,“继承”@ComponentScan注解的注解也可以被当作@ComponentScan处理,例如@SpringbootApplication。只要定义scanBasePackages就可以具有@ComponentScan的作用。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
      @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

   @AliasFor(annotation = EnableAutoConfiguration.class)
   Class<?>[] exclude() default {};

   @AliasFor(annotation = EnableAutoConfiguration.class)
   String[] excludeName() default {};

   @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
   String[] scanBasePackages() default {};


   @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
   Class<?>[] scanBasePackageClasses() default {};

}

最终@ComponentScan的处理都放在org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

处理分析

org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass里面对于@ComponentScan注解的处理代码如下。

// Process any @ComponentScan annotations
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
       //马上执行扫描
       //获取扫描路径下的定义的Bean的BeanDefinition信息
      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();
         }
         if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
            parse(bdCand.getBeanClassName(), holder.getBeanName());
         }
      }
   }
}

接着跟踪以下调用:

  • org.springframework.context.annotation.ComponentScanAnnotationParser#parse(获取扫描路径下bean的BeanDefinition信息)

    public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
      //使用默认过滤器
     ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
           componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
    
     Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
     boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
     scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
           BeanUtils.instantiateClass(generatorClass));
    
     ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
     if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
        scanner.setScopedProxyMode(scopedProxyMode);
     }
     else {
        Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
        scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
     }
    
     scanner.setResourcePattern(componentScan.getString("resourcePattern"));
    
     for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {
           scanner.addIncludeFilter(typeFilter);
        }
     }
     for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {
           scanner.addExcludeFilter(typeFilter);
        }
     }
    
     boolean lazyInit = componentScan.getBoolean("lazyInit");
     if (lazyInit) {
        scanner.getBeanDefinitionDefaults().setLazyInit(true);
     }
    
     Set<String> basePackages = new LinkedHashSet<>();
      //获取扫描路径
     String[] basePackagesArray = componentScan.getStringArray("basePackages");
     for (String pkg : basePackagesArray) {
        String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
              ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        Collections.addAll(basePackages, tokenized);
     }
      //获取扫描类
     for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
        basePackages.add(ClassUtils.getPackageName(clazz));
     }
     if (basePackages.isEmpty()) {
        basePackages.add(ClassUtils.getPackageName(declaringClass));
     }
    
     scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
        @Override
        protected boolean matchClassName(String className) {
           return declaringClass.equals(className);
        }
     });
     return scanner.doScan(StringUtils.toStringArray(basePackages));
    }
  • org.springframework.context.annotation.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);
            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);
                    //对返回的BeanDefinition注册到BeanFactory,后续统一实例化。
                    registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }
        return beanDefinitions;
    }
  • org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents(进行扫描获取BeanDefinition)

  • org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents

    private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
     Set<BeanDefinition> candidates = new LinkedHashSet<>();
     try {
         //拼接读取.class文件的资源路径,例如classpath*:garine/learn/test/**/*.class
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
              resolveBasePackage(basePackage) + '/' + this.resourcePattern;
         //读取路径下的所有.class文件信息
        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 {
                  //org.springframework.core.type.classreading.CachingMetadataReaderFactory#getMetadataReader
    //获取.class对应的元信息,例如注解信息等
                 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                  //根据注解元信息判断是不是符合条件的.class
                 if (isCandidateComponent(metadataReader)) {
                    ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                    sbd.setResource(resource);
                    sbd.setSource(resource);
                     //判断能否实例化
                    if (isCandidateComponent(sbd)) {
                        //添加到候选BeanDefinition
                       candidates.add(sbd);
                    }
                    else {
                       //忽略
                    }
                 }
                 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;
    }
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
     for (TypeFilter tf : this.excludeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
           return false;
        }
     }
     for (TypeFilter tf : this.includeFilters) {
                //默认的包含filter有org.springframework.stereotype.Component和javax.annotation.ManagedBean
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
           return isConditionMatch(metadataReader);
        }
     }
     return false;
    }
protected void registerDefaultFilters() {
    //默认的@Component注解包含filter
    //默认就有@Component注解扫描
        this.includeFilters.add(new AnnotationTypeFilter(Component.class));
        ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
        try {
            this.includeFilters.add(new AnnotationTypeFilter(
                    ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
            logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
        }
        catch (ClassNotFoundException ex) {
            // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
        }
        try {
            this.includeFilters.add(new AnnotationTypeFilter(
                    ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
            logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
        }
        catch (ClassNotFoundException ex) {
            // JSR-330 API not available - simply skip.
        }
    }
引用:@SpringBootConfiguration是一个注解,用于标识一个类是Spring Boot的配置类。它具有@Configuration的功能,并且在Spring Boot项目中更推荐使用@SpringBootConfiguration来替代@Configuration。 引用:@EnableAutoConfiguration是一个注解,用于启用自动配置。它会根据项目的依赖和配置,自动加载适合的配置类。它会扫描classpath下的META-INF/spring.factories文件,根据其中的配置信息,自动配置Spring Boot应用程序的各种组件。 引用:@ComponentScan是一个注解,用于指定要扫描的包路径。它可以扫描指定包及其子包下的所有类,并将带有标识注解(如@Controller、@Service、@Repository等)的类注册为Spring Bean。它通常与@SpringBootConfiguration一起使用,用于启用组件扫描并将扫描到的类注册为Spring的组件。 综上所述,@SpringBootConfiguration用于标识一个类是Spring Boot的配置类,@EnableAutoConfiguration用于启用自动配置,@ComponentScan用于指定要扫描的包路径。它们的功能分别是配置Spring Boot应用程序、自动加载适合的配置类以及扫描并注册指定包下的组件。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [springboot(一)-@SpringBootConfiguration、@EnableAutoConfiguration源码分析](https://blog.csdn.net/weixin_43520586/article/details/121156638)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Springboot核心注解(@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan)、关闭自动...](https://blog.csdn.net/weixin_44045828/article/details/117983882)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值