@ComponentScan的用法和源码分析

我们知道,Spring会自动探测那些添加了相应注解的类并注册到容器中,比如:

@Service
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
@Repository
public class JpaMovieFinder implements MovieFinder {
    // implementation elided for clarity
}

但前提是你必须在注解了@Configuration的类上添加@ComponentScan注解,并通过basePackages属性来定义扫描的路径。如下所示:

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    ...
}

如果是使用xml配置的话,那么配置如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.example"/>

</beans>

在默认情况下,那些注解有@Component, @Repository, @Service, @Controller和所有包含@Component的自定义注解都会被认为是目标类。当然你可以通过配置自定义的过滤器。通过添加@ComponentScan的注解属性includeFiltersexcludeFilters进行配置。每个过滤器元素需要配置typeexpression属性。如下所示:

过滤器类型表达式案例描述
annotation (default)org.example.SomeAnnotation在目标类上存在的注解
assignableorg.example.SomeClass目标类必须继承或实现的接口
aspectjorg.example…*Service+与目标类匹配的AspectJ表达式
regexorg.example.Default.*与目标类匹配的正则表达式
customorg.example.MyTypeFilter自定义的过滤器,实现Spring接口TypeFilter

如下所示:

@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}

以上这个配置就会忽略到所有注解为@Repository的类(默认情况下也会作为Spring的Bean添加条件注解),并将目录格式为.*Stub.*Repository也作为目标扫描。

You can also disable the default filters by setting useDefaultFilters=false on the annotation

在这个注解中还包括一些其他的属性:

  1. 用于为Bean生成名字的BeanNameGenerator,默认BeanNameGenerator
  2. 用于解析Scope属性的ScopeMetadataResolver,默认AnnotationScopeMetadataResolver
  3. 生成代理的模式ScopedProxyMode,默认为DEFAULT,也就是NO
  4. 扫描资源的类型resourcePattern,默认为**/*.class
  5. 是否使用默认的过滤器(也就是上面默认的情形)默认为true
/**
 * The {@link BeanNameGenerator} class to be used for naming detected components
 * within the Spring container.
 * <p>The default value of the {@link BeanNameGenerator} interface itself indicates
 * that the scanner used to process this {@code @ComponentScan} annotation should
 * use its inherited bean name generator, e.g. the default
 * {@link AnnotationBeanNameGenerator} or any custom instance supplied to the
 * application context at bootstrap time.
 * @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator)
 */
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

/**
 * The {@link ScopeMetadataResolver} to be used for resolving the scope of detected components.
 */
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

/**
 * Indicates whether proxies should be generated for detected components, which may be
 * necessary when using scopes in a proxy-style fashion.
 * <p>The default is defer to the default behavior of the component scanner used to
 * execute the actual scan.
 * <p>Note that setting this attribute overrides any value set for {@link #scopeResolver}.
 * @see ClassPathBeanDefinitionScanner#setScopedProxyMode(ScopedProxyMode)
 */
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

/**
 * Controls the class files eligible for component detection.
 * <p>Consider use of {@link #includeFilters} and {@link #excludeFilters}
 * for a more flexible approach.
 */
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;

/**
 * Indicates whether automatic detection of classes annotated with {@code @Component}
 * {@code @Repository}, {@code @Service}, or {@code @Controller} should be enabled.
 */
boolean useDefaultFilters() default true;

那么Spring是怎么完成以上扫描的任务的呢?
对于注解的方式,主要的实现在类ComponentScanAnnotationParser中,而对于xml方式的实现主要是在类ComponentScanBeanDefinitionParser中.

本文主要解析注解的方式,也就是ComponentScanAnnotationParser

首先要弄清楚这个类是如何在Spring中被使用到的,也就是使用的上下文。

在Spring容器创建的时候(主要指的类AnnotationConfigApplicationContext),就会往容器中注入一些Bean,参考本人的博客:
https://blog.csdn.net/m0_37607945/article/details/106418334
其中就会注册一个ConfigurationClassPostProcessor类型的bean.
这个Bean就是用于来扫描带有@Configuration注解的类的(ConfigurationClassPostProcessor是一个BeanDefinitionRegistryPostProcessor)。具体参看本文的博客:
https://blog.csdn.net/m0_37607945/article/details/106797189
当根据Configuration类创建configuration model的时候,就会创建ComponentScanAnnotationParser,也就是说需要这个类来限定扫描的注解和路径、以及注册Bean的名称、scopeResolver、scopedProxy、resourcePattern等属性的。
调用栈如下:
refreshContext->refresh->invokeBeanFactoryPostProcessors->invokeBeanFactoryPostProcessors->invokeBeanDefinitionRegistryPostProcessors->ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry->processConfigBeanDefinitions
对应源码如下:

public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
		ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
		BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {

	this.metadataReaderFactory = metadataReaderFactory;
	this.problemReporter = problemReporter;
	this.environment = environment;
	this.resourceLoader = resourceLoader;
	this.registry = registry;
	// 创建ComponentScanAnnotationParser对象
	this.componentScanParser = new ComponentScanAnnotationParser(
			environment, resourceLoader, componentScanBeanNameGenerator, registry);
	this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}

构造函数如下:

public ComponentScanAnnotationParser(Environment environment, ResourceLoader resourceLoader,
		BeanNameGenerator beanNameGenerator, BeanDefinitionRegistry registry) {
	this.environment = environment;
	this.resourceLoader = resourceLoader;
	this.beanNameGenerator = beanNameGenerator;
	this.registry = registry;
}

在进行ConfigurationClass的处理的时候,就会针对@ComponentScan或者```@ComponentScans````注解进行处理

解析@ComponentScan并包装到AnnotationAttributes对象中
// 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)) {
	// 针对componentScans进行处理		
	for (AnnotationAttributes componentScan : componentScans) {
		// The config class is annotated with @ComponentScan -> perform the scan immediately
		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());
			}
		}
	}
}

在SpringBoot项目中,@SpringBootApplication注解中就包含有@ComponentScan注解

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication

在这里插入图片描述
在这里插入图片描述
此时componentScans不为空而且注解中也不包含@Conditional信息,因此不需要进行skip.
遍历注解属性值componentScans进行处理。每一个注解的属性最后包装成AnnotationAttributes类型对象,这个类继承自LinkedHashMap<String, Object>。对应的属性名称为key,而对应的属性值为value,value可以是各种类型。

LinkedHashMap subclass representing annotation attribute key-value pairs as read by AnnotationUtils, AnnotatedElementUtils, and Spring’s reflection- and ASM-based org.springframework.core.type.AnnotationMetadata implementations.

Provides ‘pseudo-reification’ to avoid noisy Map generics in the calling code as well as convenience methods for looking up annotation attributes in a type-safe fashion.

封装之后提供各种类型属性值的获取方法,这样可以避免各种泛型带来的转换问题。比如可以获取布尔值、数值等等。

/**
 * Get the value stored under the specified {@code attributeName} as a boolean.
 * @param attributeName the name of the attribute to get;
 * never {@code null} or empty
 * @return the value
 * @throws IllegalArgumentException if the attribute does not exist or
 * if it is not of the expected type
 */
public boolean getBoolean(String attributeName) {
	return getRequiredAttribute(attributeName, Boolean.class);
}

/**
 * Get the value stored under the specified {@code attributeName} as a number.
 * @param attributeName the name of the attribute to get;
 * never {@code null} or empty
 * @return the value
 * @throws IllegalArgumentException if the attribute does not exist or
 * if it is not of the expected type
 */
@SuppressWarnings("unchecked")
public <N extends Number> N getNumber(String attributeName) {
	return (N) getRequiredAttribute(attributeName, Number.class);
}

最终都是调用了getRequiredAttribute方法

/**
 * Get the value stored under the specified {@code attributeName},
 * ensuring that the value is of the {@code expectedType}.
 * <p>If the {@code expectedType} is an array and the value stored
 * under the specified {@code attributeName} is a single element of the
 * component type of the expected array type, the single element will be
 * wrapped in a single-element array of the appropriate type before
 * returning it.
 * @param attributeName the name of the attribute to get;
 * never {@code null} or empty
 * @param expectedType the expected type; never {@code null}
 * @return the value
 * @throws IllegalArgumentException if the attribute does not exist or
 * if it is not of the expected type
 */
@SuppressWarnings("unchecked")
private <T> T getRequiredAttribute(String attributeName, Class<T> expectedType) {
	Assert.hasText(attributeName, "'attributeName' must not be null or empty");
	Object value = get(attributeName);
	assertAttributePresence(attributeName, value);
	assertNotException(attributeName, value);
	// 数组类型 数组元素类型为指定类型 则创建对应数组
	// 如果不是数组类型 则强转
	if (!expectedType.isInstance(value) && expectedType.isArray() &&
			expectedType.getComponentType().isInstance(value)) {
		Object array = Array.newInstance(expectedType.getComponentType(), 1);
		Array.set(array, 0, value);
		value = array;
	}
	assertAttributeType(attributeName, value, expectedType);
	return (T) value;
}
解析@ComponentScan注解并实例化ClassPathBeanDefinitionScanner对象

通过类ComponentScanAnnotationParser对象对属性进行解析,并返回bean定义包装类集合

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { 
    // 首先构造ClassPathBeanDefinitionScanner对象 采用默认过滤器用于扫描classpath的类
	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));
}

在这里插入图片描述构造一个ClassPathBeanDefinitionScanner对象。

/**
 * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory and
 * using the given {@link Environment} when evaluating bean definition profile metadata.
 * @param registry the {@code BeanFactory} to load bean definitions into, in the form
 * of a {@code BeanDefinitionRegistry}
 * @param useDefaultFilters whether to include the default filters for the
 * {@link org.springframework.stereotype.Component @Component},
 * {@link org.springframework.stereotype.Repository @Repository},
 * {@link org.springframework.stereotype.Service @Service}, and
 * {@link org.springframework.stereotype.Controller @Controller} stereotype annotations
 * @param environment the Spring {@link Environment} to use when evaluating bean
 * definition profile metadata
 * @param resourceLoader the {@link ResourceLoader} to use
 * @since 4.3.6
 */
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
		Environment environment, @Nullable ResourceLoader resourceLoader) {

	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
	this.registry = registry;

	if (useDefaultFilters) {
		// 使用默认的过滤器
		registerDefaultFilters();
	}
	setEnvironment(environment);
	setResourceLoader(resourceLoader);
}

首先会填充属性值如下图所示:
在这里插入图片描述其中引入了两个工具类:

private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();

然后设置BeanDefinitionRegistry
再设置默认的类型过滤器(因为传入的属性useDefaultFilterstrue,也是默认情况),此时会注册一个默认的过滤器,用于解析所有带有@Component(包括注解中包含,像@Service)的类并作为Spring的候选Bean.其实就是添加一个参数为Component.class的AnnotationTypeFilter对象。
同时也支持Java EE6的@ManagedBean和JSR-330的@Named注解。(前提是当前classpath要存在这两个类)其实就是添加了两个AnnotationTypeFilter类型对象到includeFilters属性当中。

/**
 * Register the default filter for {@link Component @Component}.
 * <p>This will implicitly register all annotations that have the
 * {@link Component @Component} meta-annotation including the
 * {@link Repository @Repository}, {@link Service @Service}, and
 * {@link Controller @Controller} stereotype annotations.
 * <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and
 * JSR-330's {@link javax.inject.Named} annotations, if available.
 *
 */
@SuppressWarnings("unchecked")
protected void registerDefaultFilters() {
	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.
	}
}

创建AnnotationTypeFilter对象,除了对应的注解类型类之外,还有另外两个参数,一个是是否考虑元数据注解(此时为true,意思也就是说支持像service这样注解中包含component注解的注解),同时considerInterfaces为false,也就是这个注解必须用在实现类上面而不是接口上面。这个类的功能与java.lang.Class#isAnnotationPresent差不多。

/**
 * Create a new AnnotationTypeFilter for the given annotation type.
 * This filter will also match meta-annotations. To disable the
 * meta-annotation matching, use the constructor that accepts a
 * '{@code considerMetaAnnotations}' argument. The filter will
 * not match interfaces.
 * @param annotationType the annotation type to match
 */
public AnnotationTypeFilter(Class<? extends Annotation> annotationType) {
	this(annotationType, true, false);
}

设置完默认的过滤器之后,就是设置环境属性和类加载器了。
设置环境属性没啥可注意的,但是设置类加载器,又涉及到设置另外三个类了

/**
 * Set the {@link ResourceLoader} to use for resource locations.
 * This will typically be a {@link ResourcePatternResolver} implementation.
 * <p>Default is a {@code PathMatchingResourcePatternResolver}, also capable of
 * resource pattern resolving through the {@code ResourcePatternResolver} interface.
 * @see org.springframework.core.io.support.ResourcePatternResolver
 * @see org.springframework.core.io.support.PathMatchingResourcePatternResolver
 */
@Override
public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
	this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
	this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
	this.componentsIndex = CandidateComponentsIndexLoader.loadIndex(this.resourcePatternResolver.getClassLoader());
}

获取ResourcePatternResolver对象

/**
 * Return a default {@link ResourcePatternResolver} for the given {@link ResourceLoader}.
 * <p>This might be the {@code ResourceLoader} itself, if it implements the
 * {@code ResourcePatternResolver} extension, or a default
 * {@link PathMatchingResourcePatternResolver} built on the given {@code ResourceLoader}.
 * @param resourceLoader the ResourceLoader to build a pattern resolver for
 * (may be {@code null} to indicate a default ResourceLoader)
 * @return the ResourcePatternResolver
 * @see PathMatchingResourcePatternResolver
 */
public static ResourcePatternResolver getResourcePatternResolver(@Nullable ResourceLoader resourceLoader) {
	if (resourceLoader instanceof ResourcePatternResolver) {
		return (ResourcePatternResolver) resourceLoader;
	}
	else if (resourceLoader != null) {
		return new PathMatchingResourcePatternResolver(resourceLoader);
	}
	else {
		return new PathMatchingResourcePatternResolver();
	}
}

此处传入的对象其实为Spring容器上下文类对象,属于ResourcePatternResolver类实例,因为直接返回自己。
在这里插入图片描述在这里插入图片描述关于CandidateComponentsIndexLoader,这个类会去读取META-INF/spring.components路径下的配置资源,并根据配置资源解析成Properties对象。默认情况下,这个功能是开启的,但是也仅仅是内部使用,可以通过在中配置参数spring.index.ignore禁用。注意这个类是在5.0中才开始有的。另外在这个类中使用到的SpringProperties类(静态读取spring.properties文件)在3.2.7的时候就有了。

/**
 * The location to look for components.
 * <p>Can be present in multiple JAR files.
 */
public static final String COMPONENTS_RESOURCE_LOCATION = "META-INF/spring.components";

/**
 * System property that instructs Spring to ignore the index, i.e.
 * to always return {@code null} from {@link #loadIndex(ClassLoader)}.
 * <p>The default is "false", allowing for regular use of the index. Switching this
 * flag to {@code true} fulfills a corner case scenario when an index is partially
 * available for some libraries (or use cases) but couldn't be built for the whole
 * application. In this case, the application context fallbacks to a regular
 * classpath arrangement (i.e. as no index was present at all).
 */
public static final String IGNORE_INDEX = "spring.index.ignore";


private static final boolean shouldIgnoreIndex = SpringProperties.getFlag(IGNORE_INDEX);

@Nullable
public static CandidateComponentsIndex loadIndex(@Nullable ClassLoader classLoader) {
	ClassLoader classLoaderToUse = classLoader;
	if (classLoaderToUse == null) {
		classLoaderToUse = CandidateComponentsIndexLoader.class.getClassLoader();
	}
	return cache.computeIfAbsent(classLoaderToUse, CandidateComponentsIndexLoader::doLoadIndex);
}

@Nullable
private static CandidateComponentsIndex doLoadIndex(ClassLoader classLoader) {
	if (shouldIgnoreIndex) {
		return null;
	}

	try {
		Enumeration<URL> urls = classLoader.getResources(COMPONENTS_RESOURCE_LOCATION);
		if (!urls.hasMoreElements()) {
			return null;
		}
		List<Properties> result = new ArrayList<>();
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
			result.add(properties);
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Loaded " + result.size() + "] index(es)");
		}
		int totalCount = result.stream().mapToInt(Properties::size).sum();
		return (totalCount > 0 ? new CandidateComponentsIndex(result) : null);
	}
	catch (IOException ex) {
		throw new IllegalStateException("Unable to load indexes from location [" +
				COMPONENTS_RESOURCE_LOCATION + "]", ex);
	}
}

在这里插入图片描述在这里插入图片描述
ClassPathBeanDefinitionScanner对象构造完毕。
然后就会从componentScan注解属性中获取各种属性值。比如读取nameGenerator
默认情况下会使用内部定义的
在这里插入图片描述读取scopedProxy属性
在这里插入图片描述
读取resourcePattern属性
在这里插入图片描述读取额外配置的includeFilters属性,并添加到includeFilters属性中。SpringBoot默认不添加其他包含过滤器。但是有另外四个排除类型过滤器。通过读取注解的属性excludeFilters而来。
在这里插入图片描述包含了四个key,通过解析这些属性,返回一个TypeFilter对象

private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
	List<TypeFilter> typeFilters = new ArrayList<>();
	// 过滤器类型 比如上面type为CUSTOM,则代表当前的类型过滤器是自定义的
	FilterType filterType = filterAttributes.getEnum("type");
	// 在classes中可以定义多个 每一个都代表一个类型过滤器
	for (Class<?> filterClass : filterAttributes.getClassArray("classes")) {
		switch (filterType) {
			case ANNOTATION:
				Assert.isAssignable(Annotation.class, filterClass,
						"@ComponentScan ANNOTATION type filter requires an annotation type");
				@SuppressWarnings("unchecked")
				Class<Annotation> annotationType = (Class<Annotation>) filterClass;
				typeFilters.add(new AnnotationTypeFilter(annotationType));
				break;
			case ASSIGNABLE_TYPE:
				typeFilters.add(new AssignableTypeFilter(filterClass));
				break;
			case CUSTOM:
				Assert.isAssignable(TypeFilter.class, filterClass,
						"@ComponentScan CUSTOM type filter requires a TypeFilter implementation");
				TypeFilter filter = BeanUtils.instantiateClass(filterClass, TypeFilter.class);
				// 设置Aware属性
				ParserStrategyUtils.invokeAwareMethods(
						filter, this.environment, this.resourceLoader, this.registry);
				typeFilters.add(filter);
				break;
			default:
				throw new IllegalArgumentException("Filter type not supported with Class value: " + filterType);
		}
	}

	for (String expression : filterAttributes.getStringArray("pattern")) {
		switch (filterType) {
			case ASPECTJ:
				typeFilters.add(new AspectJTypeFilter(expression, this.resourceLoader.getClassLoader()));
				break;
			case REGEX:
				typeFilters.add(new RegexPatternTypeFilter(Pattern.compile(expression)));
				break;
			default:
				throw new IllegalArgumentException("Filter type not supported with String pattern: " + filterType);
		}
	}

	return typeFilters;
}

在这里插入图片描述在这里插入图片描述解析结果如下:
在这里插入图片描述代表类DefaultListableBeanFactory是不需要进行扫描的。通过另一个excludeFilters属性解析另一个过滤器。
在这里插入图片描述类型为AutoConfigurationExcludeFilter,这个类用于匹配注册auto-configuration的类。Spring Boot的SPI自动注入的一个点。

对应逻辑如下:

@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
		throws IOException {
	return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
}

也就是包含Configuration注解

private boolean isConfiguration(MetadataReader metadataReader) {
	return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
}

并且类名称包含spring.factories文件中的EnableAutoConfiguration属性列表内。

private boolean isAutoConfiguration(MetadataReader metadataReader) {
	return getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
}

protected List<String> getAutoConfigurations() {
	if (this.autoConfigurations == null) {
		this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,
				this.beanClassLoader);
	}
	return this.autoConfigurations;
}

读取是否进行懒加载属性lazyInit,默认为false,也就是通过这个类扫描的bean会是急加载的,而非懒加载。
在这里插入图片描述然后读取basePackagesbasePackageClasses属性,也就是要扫描的路径,如果通过以上两参数都没有读到有效的路径,则将当前ConfigurationClass所在的目录作为要加载的路径。此处也就是SpringBoot主类所在的路径
在这里插入图片描述添加一个扫描过滤器,因为当前类不需要再进行扫描和注册了
在这里插入图片描述以上通过读取注解@ComponentScan中的各种属性构造并初始化了一个ClassPathBeanDefinitionScanner类对象,并随后从注解属性中读取各种值设置到这个对象属性中。也不难看出其实这个注解就是各种属性的容器,然后类ComponentScanAnnotationParser的目的也就是解析注解,而真正干活的反而是ClassPathBeanDefinitionScanner类对象了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lang20150928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值