我们知道,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
的注解属性includeFilters
和excludeFilters
进行配置。每个过滤器元素需要配置type
和expression
属性。如下所示:
过滤器类型 | 表达式案例 | 描述 |
---|---|---|
annotation (default) | org.example.SomeAnnotation | 在目标类上存在的注解 |
assignable | org.example.SomeClass | 目标类必须继承或实现的接口 |
aspectj | org.example…*Service+ | 与目标类匹配的AspectJ表达式 |
regex | org.example.Default.* | 与目标类匹配的正则表达式 |
custom | org.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
在这个注解中还包括一些其他的属性:
- 用于为Bean生成名字的
BeanNameGenerator
,默认BeanNameGenerator
- 用于解析Scope属性的
ScopeMetadataResolver
,默认AnnotationScopeMetadataResolver
- 生成代理的模式
ScopedProxyMode
,默认为DEFAULT,也就是NO - 扫描资源的类型resourcePattern,默认为**/*.class
- 是否使用默认的过滤器(也就是上面默认的情形)默认为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
再设置默认的类型过滤器(因为传入的属性useDefaultFilters
为true
,也是默认情况),此时会注册一个默认的过滤器,用于解析所有带有@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会是急加载的,而非懒加载。
然后读取
basePackages
和basePackageClasses
属性,也就是要扫描的路径,如果通过以上两参数都没有读到有效的路径,则将当前ConfigurationClass
所在的目录作为要加载的路径。此处也就是SpringBoot主类所在的路径
添加一个扫描过滤器,因为当前类不需要再进行扫描和注册了
以上通过读取注解
@ComponentScan
中的各种属性构造并初始化了一个ClassPathBeanDefinitionScanner
类对象,并随后从注解属性中读取各种值设置到这个对象属性中。也不难看出其实这个注解就是各种属性的容器,然后类ComponentScanAnnotationParser
的目的也就是解析注解,而真正干活的反而是ClassPathBeanDefinitionScanner
类对象了。