简介
lassPathScanningCandidateComponentProvider 是 Spring 框架中的一个类,它用于扫描类路径(classpath)上的组件并识别出候选的 Bean 定义。这个类通常与 ClassPathBeanDefinitionScanner 一起使用,但提供了更多的灵活性和自定义选项来控制扫描过程。
ClassPathScanningCandidateComponentProvider 的主要职责是扫描指定的包(package)或类,并根据提供的过滤器来识别哪些类应该被注册为 Spring 容器中的 Bean。它使用 TypeFilter 和 AnnotationTypeFilter 等过滤器来确定哪些类符合特定的条件。
常用方法
- findCandidateComponents(String basePackage): 扫描指定包路径下的所有类,并返回符合过滤器条件的候选组件。
- addIncludeFilter(TypeFilter filter): 添加一个包含过滤器,该过滤器将决定哪些类应该被包括在内。
- addExcludeFilter(TypeFilter filter): 添加一个排除过滤器,该过滤器将决定哪些类应该被排除在外。
- getResourceLoader(): 获取用于加载资源的资源加载器。
- getEnvironment(): 获取用于访问环境属性的环境对象。
你可以通过 ClassPathScanningCandidateComponentProvider 的构造函数来指定自定义的资源加载器和环境对象,以提供更灵活的扫描配置。
源码
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
protected final Log logger = LogFactory.getLog(getClass());
//资源匹配模式
private String resourcePattern = DEFAULT_RESOURCE_PATTERN;
//要使包含的过滤器
private final List<TypeFilter> includeFilters = new LinkedList<>();
//要移除的过滤器
private final List<TypeFilter> excludeFilters = new LinkedList<>();
@Nullable
private Environment environment;
@Nullable
private ConditionEvaluator conditionEvaluator;
//资源解析器
@Nullable
private ResourcePatternResolver resourcePatternResolver;
@Nullable
private MetadataReaderFactory metadataReaderFactory;
@Nullable
private CandidateComponentsIndex componentsIndex;
/**
* Protected constructor for flexible subclass initialization.
* @since 4.3.6
*/
protected ClassPathScanningCandidateComponentProvider() {
}
public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment) {
if (useDefaultFilters) {
registerDefaultFilters();
}
setEnvironment(environment);
setResourceLoader(null);
}
protected void registerDefaultFilters() {
//添加@Compoent注解到includeFilters中
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.trace("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.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
/**
* Scan the class path for candidate components.
* @param basePackage the package to check for annotated classes
* @return a corresponding Set of autodetected bean definitions
*/
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
//开启spring.components配置,并且includeFilters过滤器仅支持@Indexed及其子注解条件时:从配置中扫描
//一般不配置
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage);
}
}
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);
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);
//校验是否满足includeFilters和excludeFilters条件
if (isCandidateComponent(metadataReader)) {
//根据元数据创建beanDefinition
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
//设置bean的基本属性
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;
}
}
示例
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
public class CustomComponentScanner {
public static void main(String[] args) {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
MetadataReaderFactory metadataReaderFactory = resolver.getMetadataReaderFactory();
ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(MyCustomAnnotation.class));
String basePackage = "com.example.myapp";
Set<BeanDefinition> beanDefinitions = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition beanDefinition : beanDefinitions) {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(
beanDefinition.getResource());
String className = metadataReader.getClassMetadata().getClassName();
System.out.println("Found candidate component: " + className);
}
}
}
在这个示例中,我们创建了一个自定义的组件扫描器,它使用 ClassPathScanningCandidateComponentProvider 来扫描带有自定义注解 MyCustomAnnotation 的类。我们添加了一个包含过滤器来仅包括带有该注解的类,并指定了要扫描的包路径。然后,我们遍历找到的候选组件并打印它们的类名。
注意,ClassPathScanningCandidateComponentProvider 本身不会将找到的组件注册到 Spring 容器中。它只是提供了识别候选组件的功能。注册组件到容器中通常由 ClassPathBeanDefinitionScanner 或其他类似的机制完成。
本文含有隐藏内容,请 开通VIP 后查看