Quantcast
Channel: 小蓝博客
Viewing all articles
Browse latest Browse all 3155

Spring源码中的scan扫描机制

$
0
0

在 Spring 框架中,组件扫描机制(scan)是核心功能之一,它通过自动扫描类路径下的特定包或类,发现并注册带有特定注解的 Spring 组件(如 @Component@Service@Repository等)到容器中。Spring 的扫描机制由多层次的类和方法共同实现,下面将从源码角度深入剖析 Spring 的扫描机制。

1. Spring 的组件扫描机制简介

Spring 中的组件扫描主要通过 @ComponentScan注解和 <context:component-scan>标签来启动。@ComponentScan注解通常放置在配置类上,它告诉 Spring 在指定的包路径下扫描带有 @Component@Service@Controller等注解的类,并将这些类注册为 Spring 的 Bean。这个扫描机制大大简化了开发者手动注册 Bean 的工作。

2. @ComponentScanClassPathBeanDefinitionScanner

Spring 的组件扫描机制依赖于 @ComponentScan注解或 XML 配置中的 <context:component-scan>标签,Spring 使用 ClassPathBeanDefinitionScanner类来执行具体的扫描工作。核心工作流程如下:

2.1 @ComponentScan 注解

当 Spring 启动时,首先会扫描配置类中的 @ComponentScan注解。Spring 会通过注解解析器获取 @ComponentScan的值(即要扫描的包路径),并根据这个包路径找到所有需要扫描的类。

@ComponentScan(basePackages = "com.example")
public class AppConfig {
}
  • basePackages:指定要扫描的包,Spring 会从这些包路径中递归查找符合条件的类。

解释@ComponentScan是用于定义扫描路径的注解,Spring 根据这个注解启动扫描机制。

2.2 ClassPathBeanDefinitionScanner

Spring 使用 ClassPathBeanDefinitionScanner类来执行组件扫描,它是组件扫描的核心执行者。ClassPathBeanDefinitionScanner会扫描指定路径下的所有类,并通过一系列的过滤规则决定哪些类应该注册为 Spring 容器中的 Bean。

  • ClassPathBeanDefinitionScanner 继承了 ClassPathScanningCandidateComponentProvider,这个类负责通过类路径扫描候选组件。
  • 关键方法是 doScan(),它会根据传入的包路径扫描包下的所有类,并筛选出符合条件的类(通常是带有 @Component@Service等注解的类)。
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {

    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            for (BeanDefinition candidate : candidates) {
                beanDefinitions.add(registerBeanDefinition(candidate));
            }
        }
        return beanDefinitions;
    }
}

解释

  • doScan方法负责在指定包路径下扫描所有符合条件的类,并将其注册为 Bean。
  • findCandidateComponents会查找符合条件的候选组件。
  • registerBeanDefinition将找到的 Bean 注册到 Spring 容器中。

2.3 findCandidateComponents 方法

ClassPathScanningCandidateComponentProvider 类中的 findCandidateComponents() 方法是扫描过程中最重要的部分,它通过指定的包路径找到所有符合条件的类并生成 BeanDefinition。具体逻辑如下:

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        for (Resource resource : resourcePatternResolver.getResources(basePackage)) {
            // 根据资源解析元数据并生成BeanDefinition
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
            if (isCandidateComponent(metadataReader)) {
                ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                candidates.add(sbd);
            }
        }
    } catch (IOException ex) {
        // 异常处理
    }
    return candidates;
}

解释

  • findCandidateComponents方法会遍历包路径下的所有类,生成候选的 BeanDefinition,并通过过滤器决定是否将该类注册为 Spring Bean。
  • MetadataReader用于读取类的元数据(如注解),以便判断该类是否是符合条件的 Spring 组件。

2.4 过滤机制

Spring 扫描机制中使用过滤器来确定哪些类可以作为候选组件。默认情况下,Spring 会过滤出带有 @Component@Repository@Service等注解的类。通过 TypeFilter 接口,开发者可以自定义过滤规则。

默认的过滤器逻辑如下:

public class AnnotationTypeFilter extends TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) {
        return metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName());
    }
}

解释

  • 过滤器通过 MetadataReader 读取类的注解信息,如果类上存在指定的注解(如 @Component),则该类会被认为是候选组件。

3. BeanDefinition 注册

扫描到符合条件的类之后,Spring 会将其包装为 BeanDefinition 并注册到 Spring 容器中。这个过程发生在 registerBeanDefinition() 方法中。

protected void registerBeanDefinition(BeanDefinition candidate) {
    String beanName = resolveBeanName(candidate);
    beanDefinitionRegistry.registerBeanDefinition(beanName, candidate);
}

解释

  • registerBeanDefinition 方法负责将生成的 BeanDefinition 注册到 BeanDefinitionRegistry 中,beanDefinitionRegistry 是 Spring 管理所有 Bean 的注册表。
  • beanName 是该 Bean 的唯一标识,通常由类名或自定义名称生成。

4. 总结与扫描机制工作流程

4.1 扫描机制工作流程

  1. 注解解析:Spring 容器启动时解析 @ComponentScan 注解,获取要扫描的包路径。
  2. 路径扫描ClassPathBeanDefinitionScanner 执行路径扫描,查找所有符合条件的类。
  3. 过滤候选组件:通过 findCandidateComponents 方法,使用注解或自定义过滤器筛选符合条件的组件。
  4. 生成 BeanDefinition:对符合条件的类生成 BeanDefinition
  5. 注册 Bean:将 BeanDefinition 注册到 BeanDefinitionRegistry 中。

4.2 Spring 扫描机制分析表

组件名称功能描述
@ComponentScan指定扫描的包路径,用于启用组件扫描功能
ClassPathBeanDefinitionScanner执行类路径扫描,查找并注册候选组件
findCandidateComponents根据路径查找符合条件的候选组件,返回 BeanDefinition
TypeFilter过滤规则,决定哪些类可以作为 Spring 组件
BeanDefinitionRegistry注册扫描到的 BeanDefinition,将其交给 Spring 容器管理

5. 结语

Spring 的扫描机制通过 @ComponentScan 注解与 ClassPathBeanDefinitionScanner 类协作,完成了从类路径扫描到组件注册的全流程。这种机制使得开发者可以轻松地通过注解配置来管理 Bean,从而提高了开发效率。


Viewing all articles
Browse latest Browse all 3155

Latest Images

Trending Articles