ClassPathScanningCandidateComponentProviderClassPathBeanDefinitionScanner 都是 Spring 框架中用于扫描类路径下的组件的工具,但它们在功能和适用场景上有一些区别。

ClassPathScanningCandidateComponentProvider

  • 适用场景:当你需要以编程方式手动控制哪些类被注册为 Spring 容器中的 bean 时,可以使用 ClassPathScanningCandidateComponentProvider。它提供了更细粒度的控制,允许你自定义过滤器来决定哪些类应该被视为候选组件。

  • 主要特性:

    • 提供了方法来设置包含和排除的过滤器(includeFilters 和 excludeFilters)。
    • 可以直接调用 findCandidateComponents 方法来获取所有符合条件的类。

ClassPathBeanDefinitionScanner

  • 适用场景:通常用于自动配置场景下,通过注解如 @ComponentScan 来扫描指定的基础包,并自动将带有特定注解的类注册为 Spring 容器中的 bean。它是基于 ClassPathScanningCandidateComponentProvider 构建的,但提供了更多的功能,比如自动注册到 BeanFactory 中。

  • 主要特性:

    • 继承自 ClassPathScanningCandidateComponentProvider,因此具有相同的核心功能。
    • 提供了 scan 方法,可以直接扫描并注册符合过滤条件的类到指定的 BeanDefinitionRegistry 中。

区别

  • 灵活性:ClassPathScanningCandidateComponentProvider 提供了更高的灵活性,因为它允许你在不将其注册到 Spring 容器的情况下处理扫描结果。你可以选择性地处理这些类,而不是自动注册它们。

  • 集成度:ClassPathBeanDefinitionScanner 更加集成化,它不仅扫描类,还会自动将这些类注册为 Spring 容器中的 bean。

示例代码解析

下面是一个简单的例子,演示如何使用这两种方式来扫描并注册类:

使用 ClassPathScanningCandidateComponentProvider

import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Component;

public class CustomScannerExample {
    public static void main(String[] args) throws Exception {
        // 创建一个扫描器实例
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        
        // 添加过滤器,只扫描带有 @Component 注解的类
        scanner.addIncludeFilter(new AnnotationTypeFilter(Component.class));
        
        // 扫描指定的基础包
        for (var beanDef : scanner.findCandidateComponents("com.example")) {
            System.out.println(beanDef.getBeanClassName());
        }
    }
}

在这个例子中,我们创建了一个 ClassPathScanningCandidateComponentProvider 实例,并添加了一个过滤器,以便仅扫描带有 @Component 注解的类。然后我们指定了要扫描的基础包 "com.example",并打印出找到的所有类名。

使用 ClassPathBeanDefinitionScanner

import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Component;

public class AutoRegisterScannerExample {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        
        // 创建一个扫描器实例,并关联到 ApplicationContext
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
        
        // 添加过滤器,只扫描带有 @Component 注解的类
        scanner.addIncludeFilter(new AnnotationTypeFilter(Component.class));
        
        // 执行扫描并注册bean
        scanner.scan("com.example");
        
        // 刷新上下文,使所有已注册的bean准备好使用
        context.refresh();
        
        // 获取并使用我们的bean
        var myService = context.getBean(MyService.class);
        myService.performTask();
        
        // 关闭上下文
        context.close();
    }
}

interface MyService {
    void performTask();
}

@Component
class MyServiceImpl implements MyService {
    @Override
    public void performTask() {
        System.out.println("Executing task in MyServiceImpl");
    }
}

在这个例子中,我们使用 ClassPathBeanDefinitionScanner 自动扫描并注册基础包 "com.example" 下所有带有 @Component 注解的类。我们还创建了一个 GenericApplicationContext 实例,并将扫描器与其关联起来。扫描完成后,我们刷新上下文并获取其中一个已注册的 bean 来执行任务。

这两个例子展示了如何根据不同的需求选择合适的类路径扫描工具。如果你只需要获取类信息而不必注册到 Spring 容器中,可以选择 ClassPathScanningCandidateComponentProvider;如果需要自动注册,则应选择 ClassPathBeanDefinitionScanner

posted on 2025-03-13 11:25  卡米i  阅读(4)  评论(0)    收藏  举报