Spring @ComponentScan注解扫描组件原理

作者:xuguofeng2016 时间:2021-09-21 09:10:02 

本文将通过阅读spring源码,分析@ComponentScan注解扫描组件的原理。

和@Bean注解一样,@ComponentScan注解也是通过ConfigurationClassPostProcessor后置处理器完成扫描工作的。

在《Spring-@Bean注解源码分析》中,详细介绍了spring注册、调用ConfigurationClassPostProcessor后置处理器的流程,本文不再重复记录。

ConfigurationClassPostProcessor类

ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,spring容器在refresh过程中,会调用所有BeanDefinitionRegistryPostProcessor处理器的postProcessBeanDefinitionRegistry方法:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// ...
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
// ...

重点逻辑在parser.parse(candidates)处,作用是解析@Configuration类。

Parse each @Configuration class

parser.parse(candidates);

最终调用到:

protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// ...
// 1. 解析@ComponentScan注解元数据
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
   // 2. 判断标注了@ComponentScan注解且不skip该类
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(
               sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
       // 可能标注多个@ComponentScan注解
for (AnnotationAttributes componentScan : componentScans) {
// 3. 通过@ComponentScan解析组件BeanDefinition集
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
           // 4. 递归解析
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());
}
}
}
}
// ...
}

重点在第3点,通过@ComponentScan解析组件BeanDefinition集:

this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

componentScanParser是ComponentScanAnnotationParser类型对象,parse方法解析ComponentScan注解,扫描组件。

ComponentScanAnnotationParser类

parse方法解析ComponentScan注解,扫描组件:

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
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类

doScan方法扫描指定包下面的组件:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 扫描组件
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils
                   .processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils
                   .applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 注册到容器
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
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);
for (Resource resource : resources) {
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                   // 判断是否是需要扫描的spring组件,遍历excludeFilters和includeFilters集来进行判断
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
candidates.add(sbd);
}
}
} catch (Throwable ex) {
throw new BeanDefinitionStoreException("");
}
}
}
} catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}

来源:https://blog.csdn.net/xuguofeng2016/article/details/128553962

标签:Spring,@ComponentScan,注解,扫描组件
0
投稿

猜你喜欢

  • MyBatis-Plus 集成动态多数据源的实现示例

    2023-06-26 17:56:35
  • MyBatis-Plus不使用数据库默认值的问题及解决

    2023-11-13 17:37:20
  • Android中通过RxJava进行响应式程序设计的入门指南

    2023-06-27 08:17:46
  • 详解SpringCloud mysql实现配置中心

    2023-11-02 05:12:57
  • c语言10个经典小程序

    2023-11-03 01:11:35
  • Springboot全局异常捕获及try catch区别解析

    2022-03-02 17:44:07
  • 浅析Mybatis 在CS程序中的应用

    2023-06-24 08:44:15
  • Java Callable接口实现细节详解

    2023-11-10 05:34:26
  • java8 实现提取集合对象的每个属性

    2023-10-17 19:37:27
  • Java虚拟机内存结构及编码实战分享

    2023-11-29 13:47:47
  • Java面试题冲刺第二十三天--分布式

    2023-09-24 07:30:43
  • SpringBoot2.1.4中的错误处理机制

    2023-11-06 02:48:47
  • Studio 编译报错:compileSdkVersion 'android-24' requires JDK 1.8 or later to compile.的解决办法

    2023-06-19 17:19:41
  • Springboot Mybatis Plus自动生成工具类详解代码

    2022-09-17 12:01:57
  • MyBatis配置的应用与对比jdbc的优势

    2023-08-27 07:03:47
  • c++中的string常用函数用法总结

    2023-11-02 16:44:02
  • IDEA安装后找不到.vmoptions文件的问题及解决

    2023-10-05 22:43:44
  • 利用Spring Boot操作MongoDB的方法教程

    2023-11-29 11:14:27
  • Java 入门图形用户界面设计之复选框

    2022-06-19 15:22:53
  • mybatis框架xml下trim中的prefix与suffix等标签的用法

    2023-09-20 18:55:24
  • asp之家 软件编程 m.aspxhome.com