ClassPathScanningCandidateComponentProvider学习

发布于:2024-02-19 ⋅ 阅读:(63) ⋅ 点赞:(0)

简介

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 后查看

网站公告

今日签到

点亮在社区的每一天
去签到