From 524844c076eecea9c5c798962163bce4b647d54f Mon Sep 17 00:00:00 2001 From: xuchengsheng Date: Wed, 8 Nov 2023 16:41:18 +0800 Subject: [PATCH] =?UTF-8?q?ClassPathBeanDefinitionScanner=E6=BA=90?= =?UTF-8?q?=E7=A0=81=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring-beans/pom.xml | 1 + .../README.md | 549 ++++++++++++++++++ .../pom.xml | 14 + .../ClassPathBeanDefinitionScannerDemo.java | 29 + .../xcs/spring/controller/MyController.java | 11 + .../xcs/spring/repository/MyRepository.java | 11 + .../com/xcs/spring/service/MyService.java | 11 + 7 files changed, 626 insertions(+) create mode 100644 spring-beans/spring-bean-classPathBeanDefinitionScanner/README.md create mode 100644 spring-beans/spring-bean-classPathBeanDefinitionScanner/pom.xml create mode 100644 spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/ClassPathBeanDefinitionScannerDemo.java create mode 100644 spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/controller/MyController.java create mode 100644 spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/repository/MyRepository.java create mode 100644 spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/service/MyService.java diff --git a/spring-beans/pom.xml b/spring-beans/pom.xml index b87e26c..1d922a5 100644 --- a/spring-beans/pom.xml +++ b/spring-beans/pom.xml @@ -20,6 +20,7 @@ spring-bean-propertiesBeanDefinitionReader spring-bean-groovyBeanDefinitionReader spring-bean-annotatedBeanDefinitionReader + spring-bean-classPathBeanDefinitionScanner \ No newline at end of file diff --git a/spring-beans/spring-bean-classPathBeanDefinitionScanner/README.md b/spring-beans/spring-bean-classPathBeanDefinitionScanner/README.md new file mode 100644 index 0000000..b1eeeeb --- /dev/null +++ b/spring-beans/spring-bean-classPathBeanDefinitionScanner/README.md @@ -0,0 +1,549 @@ +## ClassPathBeanDefinitionScanner + +- [ClassPathBeanDefinitionScanner](#classpathbeandefinitionscanner) + - [一、知识储备](#一知识储备) + - [二、基本描述](#二基本描述) + - [三、主要功能](#三主要功能) + - [四、最佳实践](#四最佳实践) + - [五、时序图](#五时序图) + - [六、源码分析](#六源码分析) + - [注册默认过滤器阶段](#注册默认过滤器阶段) + - [扫描Bean定义阶段](#扫描bean定义阶段) + - [七、与其他组件的关系](#七与其他组件的关系) + - [八、常见问题](#八常见问题) + +### 一、知识储备 + +1. **Bean定义** + - 了解Bean的概念以及如何定义和配置Bean是非常重要的。这包括Bean的ID、类名、属性注入、依赖关系等。 +2. **Spring注解** + + 我们需要熟悉 Spring 的注解,如 `@Component`、`@Configuration`、`@ComponentScan` 等,因为 `ClassPathBeanDefinitionScanner` 类就是用来解析此类注解的。 +3. **类路径和包结构** + + 了解类路径是什么,如何设置类路径,以及包的结构和命名规范,因为 `ClassPathBeanDefinitionScanner` 会在类路径上扫描包和类。 +4. **`ResourceLoader`** + - `ResourceLoader` 可以用于获取资源,这些资源可以是文件、类路径上的资源、URL、甚至是远程资源。它提供了一种统一的方式来加载资源,无论这些资源位于何处。[点击查看ResourceLoader接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-resources/spring-resource-resourceLoader) +5. **`Resource`** + - `Resource` 代表一个资源,可以是文件、类路径上的文件、URL 等。它提供了对资源的抽象和访问方法。[点击查看Resource接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-resources/spring-resource) + +### 二、基本描述 + +`ClassPathBeanDefinitionScanner` 类,用于在类路径上扫描指定包及其子包中的类,识别符合条件的类,并将其注册为 Spring Bean 的定义,从而实现组件扫描和自动装配,使我们能够方便地管理和配置应用程序中的 Bean。它允许我们定义过滤条件,以确定哪些类应被注册为 Bean,以及配合自动装配实现依赖注入,提高了应用程序的可维护性和扩展性。 + +### 三、主要功能 + +1. **包扫描** + + 扫描类路径上的指定包及其子包中的类文件,用于寻找潜在的 Spring Bean 候选类。 +2. **`BeanDefinition`注册** + + 将找到的符合条件的类注册为 Spring Bean 的定义(`BeanDefinition`)。这使得这些类的实例可以由 Spring 容器进行管理。 +3. **自动装配** + + 自动装配机制结合使用,以自动识别并注入依赖的 Bean。这降低了手动配置 Bean 依赖的需求,提高了应用程序的可维护性。 +4. **注解识别** + + 扫描器能够识别带有特定注解的类,例如 `@Component`、`@Service`、`@Repository` 等,然后将这些类注册为 Spring Bean。 +5. **过滤条件** + + 配置过滤条件,以决定哪些类应该被注册为 Bean。过滤条件可以是注解类型、类名规则、类实现的接口等,从而筛选出符合条件的类。 +6. **Bean 名称生成** + + 自动生成 Bean 的名称,通常是类名的首字母小写形式,但也可以通过配置进行自定义。 +7. **自定义配置** + + 通过配置 `ClassPathBeanDefinitionScanner` 的属性来自定义其行为,例如指定要扫描的包、设置过滤条件、配置 Bean 名称生成规则等。 + +### 四、最佳实践 + +创建 `DefaultListableBeanFactory` 和关联的 `ClassPathBeanDefinitionScanner` 对象,然后使用扫描器的 `scan` 方法扫描指定包中的类,将这些类注册为 Spring Bean,最终能够通过工厂获取并使用这些 Bean。 + +```java +public class ClassPathBeanDefinitionScannerDemo { + + public static void main(String[] args) { + // 创建一个 AnnotationConfigApplicationContext + DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); + + // 创建 ClassPathBeanDefinitionScanner 并将其关联到容器 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(factory); + + // 使用 ClassPathBeanDefinitionScanner的scan方法扫描Bean对象 + scanner.scan("com.xcs.spring"); + + System.out.println("MyController = " + factory.getBean(MyController.class)); + System.out.println("MyService = " + factory.getBean(MyService.class)); + System.out.println("MyRepository = " + factory.getBean(MyRepository.class)); + } +} +``` + +`@Controller`、`@Service` 和 `@Repository`。这些注解通常用于标记不同层次的组件,以便 Spring 容器可以自动识别和注册它们为 Spring Bean。 + +```java +@Controller +public class MyController { +} + +@Service +public class MyService { +} + +@Repository +public class MyRepository { +} +``` + +运行结果发现, `ClassPathBeanDefinitionScanner` 成功扫描了指定包中的类,并将这些类注册为 Spring Bean。 + +```java +MyController = com.xcs.spring.controller.MyController@1e802ef9 +MyService = com.xcs.spring.service.MyService@2b6faea6 +MyRepository = com.xcs.spring.repository.MyRepository@778d1062 +``` + +### 五、时序图 + +~~~mermaid +sequenceDiagram +Title: ClassPathBeanDefinitionScanner时序图 +par 注册默认过滤器阶段 +ClassPathBeanDefinitionScannerDemo->>ClassPathBeanDefinitionScanner:new ClassPathBeanDefinitionScanner(registry) +note right of ClassPathBeanDefinitionScannerDemo: 创建 ClassPathBeanDefinitionScanner 实例,并关联到注册表 +ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:ClassPathBeanDefinitionScanner(registry,useDefaultFilters) +note over ClassPathBeanDefinitionScanner: 创建 ClassPathBeanDefinitionScanner 实例,启用默认过滤器 +ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:ClassPathBeanDefinitionScanner(registry,useDefaultFilters,environment) +note over ClassPathBeanDefinitionScanner: 创建 ClassPathBeanDefinitionScanner 实例,设置环境信息 +ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:ClassPathBeanDefinitionScanner(registry,useDefaultFilters,environment,resourceLoader) +note over ClassPathBeanDefinitionScanner: 创建 ClassPathBeanDefinitionScanner 实例,设置资源加载器 +ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:registerDefaultFilters() +note over ClassPathBeanDefinitionScanner: 注册默认的组件过滤器 +ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScannerDemo:返回scanner +end +par 扫描Bean定义阶段 +note left of ClassPathBeanDefinitionScanner: 返回 ClassPathBeanDefinitionScanner 实例 +ClassPathBeanDefinitionScannerDemo->>ClassPathBeanDefinitionScanner:scan(basePackages) +note right of ClassPathBeanDefinitionScannerDemo: 执行组件扫描操作 +ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:doScan(basePackages) +note over ClassPathBeanDefinitionScanner: 执行组件扫描操作 +ClassPathBeanDefinitionScanner->>ClassPathScanningCandidateComponentProvider:findCandidateComponents(basePackage) +note left of ClassPathScanningCandidateComponentProvider: 查找符合条件的候选组件 +ClassPathScanningCandidateComponentProvider->>ClassPathScanningCandidateComponentProvider:scanCandidateComponents(basePackage) +note over ClassPathScanningCandidateComponentProvider: 扫描候选组件 +ClassPathScanningCandidateComponentProvider->>ClassPathScanningCandidateComponentProvider:isCandidateComponent(metadataReader) +note over ClassPathScanningCandidateComponentProvider: 判断是否是候选组件 +ClassPathScanningCandidateComponentProvider->>AbstractTypeHierarchyTraversingFilter:match(metadataReader,metadataReaderFactory) +note left of AbstractTypeHierarchyTraversingFilter: 使用过滤器匹配 +AbstractTypeHierarchyTraversingFilter->>AnnotationTypeFilter:matchSelf(metadataReader) +note left of AnnotationTypeFilter: 调用 matchSelf(metadataReader) +AnnotationTypeFilter->>AbstractTypeHierarchyTraversingFilter:返回是否匹配 +AbstractTypeHierarchyTraversingFilter->>ClassPathScanningCandidateComponentProvider:返回是否匹配 +ClassPathScanningCandidateComponentProvider->>ClassPathScanningCandidateComponentProvider:candidates.add(sbd) +note over ClassPathScanningCandidateComponentProvider: 如果匹配,则将候选组件加入列表 +ClassPathScanningCandidateComponentProvider->>ClassPathBeanDefinitionScanner:返回扫描到的Bean定义 +ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:checkCandidate(beanName, candidate) +note over ClassPathBeanDefinitionScanner: 检查候选组件 +ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:registerBeanDefinition(definitionHolder,registry) +note over ClassPathBeanDefinitionScanner: 注册 Bean 定义 +ClassPathBeanDefinitionScanner->>AnnotationConfigUtils:registerAnnotationConfigProcessors(registry) +note right of ClassPathBeanDefinitionScanner: 启用注解驱动的配置 + +AnnotationConfigUtils->>AnnotationConfigUtils:registerAnnotationConfigProcessors(registry,source) +note over AnnotationConfigUtils: 启用注解驱动的配置 + +end +~~~ + + + +### 六、源码分析 + +#### 注册默认过滤器阶段 + +在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner(registry)`方法中,实际上是通过委派给另一个构造函数来创建,并且在第二个参数设置为 `true` ,表示会包括注册注解配置处理器(`Annotation Config Processors`),这通常用于支持注解配置,包括 `@Component`、`@Service`、`@Repository` 等注解。 + +```java +public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) { + this(registry, true); +} +``` + +在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner(registry,useDefaultFilters)`方法中,实际上是通过委派给另一个构造函数来创建,并且获取了上下文的环境变量进行传递。 + +```java +public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) { + this(registry, useDefaultFilters, getOrCreateEnvironment(registry)); +} +``` + +在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner(registry,useDefaultFilters,environment)`方法中,实际上是通过委派给另一个构造函数来创建,最后一个参数是 `ResourceLoader`,如果 `BeanDefinitionRegistry` 也是 `ResourceLoader` 的实例,那么它会将 `ResourceLoader` 传递给后面的构造函数。 + +```java +public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, + Environment environment) { + this(registry, useDefaultFilters, environment, + (registry instanceof ResourceLoader ? (ResourceLoader) registry : null)); +} +``` + +在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner(registry,useDefaultFilters,environment,resourceLoader)`方法中,我们重点关注`registerDefaultFilters()`方法,此方法主要是注册默认的过滤器,这些过滤器用于扫描默认的注解类,如 `@Component`、`@Service`、`@Repository` 等。 + +```java +public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, + Environment environment, @Nullable ResourceLoader resourceLoader) { + // 确保 BeanDefinitionRegistry 不为空 + Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); + this.registry = registry; + + // 如果需要使用默认过滤器,注册默认过滤器 + if (useDefaultFilters) { + registerDefaultFilters(); + } + + // 设置环境信息 + setEnvironment(environment); + + // 设置资源加载器(ResourceLoader) + setResourceLoader(resourceLoader); +} +``` + +在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#registerDefaultFilters`方法中, 主要目的就是注册`@Component` 注解,用于扫描和识别标记为组件的类,同时还提供了对一些 Java EE 规范中的注解的支持。 + +```java +protected void registerDefaultFilters() { + // 向 includeFilters 集合中添加默认的过滤器 + this.includeFilters.add(new AnnotationTypeFilter(Component.class)); + ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); + + // 尝试注册 JSR-250 'javax.annotation.ManagedBean' 过滤器(如果可用) + try { + this.includeFilters.add(new AnnotationTypeFilter( + ((Class) 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(包括在 Java EE 6 中)不可用,简单地跳过。 + } + + // 尝试注册 JSR-330 'javax.inject.Named' 过滤器(如果可用) + try { + this.includeFilters.add(new AnnotationTypeFilter( + ((Class) 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 不可用,简单地跳过。 + } +} +``` + +#### 扫描Bean定义阶段 + +在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan`方法中,实际是调用 `doScan` 方法来扫描指定的包,查找符合条件的类,并将它们注册为 `BeanDefinition`,另外还计算新的 `BeanDefinition` 计数与扫描前的计数之差。 + +```java +public int scan(String... basePackages) { + // 记录扫描开始时的 BeanDefinition 数量 + int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); + + // 步骤1: 执行扫描操作 + doScan(basePackages); + + // 步骤2: 注册注解配置处理器(如果需要的话) + if (this.includeAnnotationConfig) { + AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); + } + + // 返回扫描后的 BeanDefinition 数量与扫描开始前的数量的差,表示扫描期间注册的新 BeanDefinition 数量 + return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); +} +``` + +> `org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan`步骤1 + +在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan`方法中,主要负责找到符合条件的类,处理它们,并注册为 Spring Bean。 + +```java +protected Set doScan(String... basePackages) { + Assert.notEmpty(basePackages, "At least one base package must be specified"); + Set beanDefinitions = new LinkedHashSet<>(); + for (String basePackage : basePackages) { + // 查找符合条件的 BeanDefinition 候选类 + Set candidates = findCandidateComponents(basePackage); + for (BeanDefinition candidate : candidates) { + // 解析 Bean 的作用域信息 + ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); + candidate.setScope(scopeMetadata.getScopeName()); + // 生成 Bean 的名称 + String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); + if (candidate instanceof AbstractBeanDefinition) { + // 对 BeanDefinition 进行进一步处理 + postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); + } + if (candidate instanceof AnnotatedBeanDefinition) { + // 处理通用的注解 + AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); + } + if (checkCandidate(beanName, candidate)) { + // 创建 BeanDefinitionHolder 并注册为 BeanDefinition + BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); + // 处理 Bean 的作用域代理模式 + definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); + beanDefinitions.add(definitionHolder); + // 将 BeanDefinition 注册到 BeanDefinitionRegistry 中 + registerBeanDefinition(definitionHolder, this.registry); + } + } + } + return beanDefinitions; +} +``` + +在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents`方法中,先是走组件索引中获取候选 `BeanDefinition` ,还是执行标准的包扫描操作。 + +```java +public Set findCandidateComponents(String basePackage) { + if (this.componentsIndex != null && indexSupportsIncludeFilters()) { + // 如果存在组件索引并且索引支持包含过滤器 + return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); + } + else { + // 否则,执行标准的扫描操作 + return scanCandidateComponents(basePackage); + } +} +``` + +在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents`方法中,扫描指定包路径下的类,并找到候选的 `BeanDefinition`。它包含了文件扫描、元数据读取和过滤候选类的逻辑。 + +```java +private Set scanCandidateComponents(String basePackage) { + // 创建一个用于存储候选 BeanDefinition 的集合 + Set 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(); + // 遍历资源数组,查找候选 BeanDefinition + for (Resource resource : resources) { + // ... [代码部分省略以简化] + if (resource.isReadable()) { + try { + // 获取资源的元数据读取器 + MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); + if (isCandidateComponent(metadataReader)) { + // 如果是候选的组件类 + ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); + sbd.setSource(resource); + // 如果是候选的组件类,添加到候选 BeanDefinition 集合 + if (isCandidateComponent(sbd)) { + // ... [代码部分省略以简化] + candidates.add(sbd); + } + else { + // ... [代码部分省略以简化] + } + } + else { + // ... [代码部分省略以简化] + } + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } + } + else { + // ... [代码部分省略以简化] + } + } + } + catch (IOException ex) { + // ... [代码部分省略以简化] + } + // 返回找到的候选 BeanDefinition 集合 + return candidates; +} +``` + +在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent(metadataReader)`方法中,主要用于在组件扫描过程中,根据过滤器的规则和条件检查,判断是否给定的类是一个候选的组件类,用于决定是否将其注册为 `Spring` `BeanDefinition`。 + +```java +protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { + // 遍历排除过滤器列表 + for (TypeFilter tf : this.excludeFilters) { + // 如果匹配了任何一个排除过滤器,将其标记为不是候选组件 + if (tf.match(metadataReader, getMetadataReaderFactory())) { + return false; + } + } + + // 遍历包含过滤器列表 + for (TypeFilter tf : this.includeFilters) { + // 如果匹配了任何一个包含过滤器 + if (tf.match(metadataReader, getMetadataReaderFactory())) { + // 检查是否符合条件(通常用于进一步条件检查) + return isConditionMatch(metadataReader); + } + } + + // 默认情况下,不是候选组件 + return false; +} +``` + +在`org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter#match(metadataReader, metadataReaderFactory)`方法中, 主要用于在过滤过程中检查是否应该匹配给定的 `MetadataReader`。 + +```java +@Override +public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) + throws IOException { + + // 优化:首先检查是否可以直接匹配当前的 metadataReader + if (matchSelf(metadataReader)) { + return true; + } + // 获取类元数据信息 + ClassMetadata metadata = metadataReader.getClassMetadata(); + + // 检查是否匹配类的名称 + if (matchClassName(metadata.getClassName())) { + return true; + } + + // ... [代码部分省略以简化] + + // 如果没有任何条件匹配成功,返回 false 表示不匹配 + return false; +} +``` + +在`org.springframework.core.type.filter.AnnotationTypeFilter#matchSelf`方法中,主要用于检查给定的 `metadataReader` 是否满足特定的匹配条件。如果 `metadataReader` 是一个类,该类被 `@Component` 注解修饰,那么 `matchSelf` 方法将被用于检查是否匹配 `@Component` 注解或其元注解。 + +```java +@Override +protected boolean matchSelf(MetadataReader metadataReader) { + // 获取注解元数据信息 + AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); + + // 检查是否包含指定的注解或其元注解 + return metadata.hasAnnotation(this.annotationType.getName()) || + (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName())); +} +``` + +> `org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan`步骤2 + +在`org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(registry)`方法中,注解配置处理器注册到 Spring 容器中,从而启用注解驱动的配置。 + +```java +public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) { + registerAnnotationConfigProcessors(registry, null); +} +``` + +在`org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(registry, source)`方法中,主要目的是注册一些关键的注解配置处理器,以便支持注解驱动的配置和处理不同类型的注解。 + +```java +public static Set registerAnnotationConfigProcessors( + BeanDefinitionRegistry registry, @Nullable Object source) { + + // 1. 如果 BeanDefinitionRegistry 是 DefaultListableBeanFactory 的实例,执行以下操作 + DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); + if (beanFactory != null) { + // 1.1 检查当前的依赖比较器是否是 AnnotationAwareOrderComparator 的实例,如果不是,设置依赖比较器为 AnnotationAwareOrderComparator.INSTANCE,用于处理注解驱动排序。 + if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { + beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); + } + // 1.2 检查当前的自动装配候选解析器是否是 ContextAnnotationAutowireCandidateResolver 的实例,如果不是,设置自动装配候选解析器为 ContextAnnotationAutowireCandidateResolver,用于处理注解驱动的自动装配。 + if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { + beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); + } + } + + // 2. 创建一个空的 LinkedHashSet 用于存储将要注册的 Bean 定义。 + Set beanDefs = new LinkedHashSet<>(8); + + // 3. 检查是否已经注册了名为 CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME 的 Bean 定义。如果没有,创建一个 ConfigurationClassPostProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 + if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { + RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); + def.setSource(source); + beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); + } + + // 4. 检查是否已经注册了名为 AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME 的 Bean 定义。如果没有,创建一个 AutowiredAnnotationBeanPostProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 + if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { + RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); + def.setSource(source); + beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); + } + + // 5. 检查是否已经注册了名为 COMMON_ANNOTATION_PROCESSOR_BEAN_NAME 的 Bean 定义,这是用于支持 JSR-250 注解的处理器。如果没有,并且检测到 JSR-250 的支持,创建一个 CommonAnnotationBeanPostProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 + if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { + RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); + def.setSource(source); + beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); + } + + // 6. 检查是否已经注册了名为 PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME 的 Bean 定义,这是用于支持 JPA 注解的处理器。如果没有,并且检测到 JPA 的支持,创建一个 PersistenceAnnotationBeanPostProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 + if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { + RootBeanDefinition def = new RootBeanDefinition(); + try { + def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, + AnnotationConfigUtils.class.getClassLoader())); + } + catch (ClassNotFoundException ex) { + throw new IllegalStateException( + "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); + } + def.setSource(source); + beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); + } + + // 7. 检查是否已经注册了名为 EVENT_LISTENER_PROCESSOR_BEAN_NAME 的 Bean 定义。如果没有,创建一个 EventListenerMethodProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 + if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { + RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class); + def.setSource(source); + beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); + } + + // 8. 检查是否已经注册了名为 EVENT_LISTENER_FACTORY_BEAN_NAME 的 Bean 定义。如果没有,创建一个 DefaultEventListenerFactory 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 + if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) { + RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class); + def.setSource(source); + beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME)); + } + + // 9. 返回包含注册的 Bean 定义的 LinkedHashSet。 + return beanDefs; +} +``` + +### 七、与其他组件的关系 + +1. **`BeanDefinitionLoader`** + + Spring Boot 中的 `BeanDefinitionLoader` 负责加载 Bean 定义,通常与 `ClassPathBeanDefinitionScanner` 一起使用,以支持自动配置。 +2. **`AnnotationConfigApplicationContext`** + + 这是 Spring Framework 中的 `AnnotationConfigApplicationContext`,它用于配置 Spring 应用程序上下文并支持注解驱动的配置。在内部,它可能使用 `ClassPathBeanDefinitionScanner` 来扫描和注册组件。 +3. **`ComponentScanAnnotationParser` &`ComponentScanBeanDefinitionParser`** + + 这些类通常用于解析 `@ComponentScan` 注解,以指定需要扫描的包和组件。它们可能使用 `ClassPathBeanDefinitionScanner` 来执行扫描操作。 +4. **`AnnotationConfigWebApplicationContext`** + + 这是 Spring Web 应用程序中的 `AnnotationConfigWebApplicationContext`,通常用于配置 Web 应用程序的 Spring 上下文。它可以使用 `ClassPathBeanDefinitionScanner` 来进行组件扫描。 + +### 八、常见问题 + +1. **`ClassPathBeanDefinitionScanner` 是什么?** + - 用于执行组件扫描和注册。它用于查找应用程序类路径上的标记为组件的类(如标有 `@Component`、`@Service`、`@Repository` 注解的类),并将它们注册为 Spring Bean。 +2. **`ClassPathBeanDefinitionScanner` 的主要功能是什么?** + - 主要功能包括扫描指定包中的类,识别带有特定注解的类,将它们注册为 Spring Bean,并支持自动装配。它还支持自定义的过滤器,以便根据应用程序需求筛选要扫描和注册的类。 +3. **如何在 Spring 配置中使用 `ClassPathBeanDefinitionScanner`?** + - 通常,`ClassPathBeanDefinitionScanner` 不会直接在配置文件中使用。它通常由 Spring 的注解驱动配置或 Spring Boot 的自动配置机制隐式使用。配置类中使用 `@ComponentScan` 注解来启用组件扫描,`ClassPathBeanDefinitionScanner` 将在内部使用。 +4. **如何自定义 `ClassPathBeanDefinitionScanner` 的行为?** + - 可以通过创建自定义组件扫描器并扩展 `ClassPathBeanDefinitionScanner`,然后使用自定义扫描器来覆盖默认的组件扫描行为。这允许我们自定义扫描过滤器、扫描路径等。 +5. **`ClassPathBeanDefinitionScanner` 是否支持自定义注解?** + - 可以配置 `ClassPathBeanDefinitionScanner` 来扫描具有自定义注解的类。通过设置包含和排除过滤器,我们可以指定要扫描的自定义注解类型。 +6. **`ClassPathBeanDefinitionScanner` 是否支持排除特定类?** + - 可以使用 `ClassPathBeanDefinitionScanner` 的过滤器来排除特定类,以防止它们被扫描和注册为 Spring Bean。 +7. **`ClassPathBeanDefinitionScanner` 是否支持排除特定包?** + - 可以使用 `ClassPathBeanDefinitionScanner` 的过滤器来排除特定包中的类,以防止它们被扫描和注册为 Spring Bean。 +8. **如何在 Spring Boot 中使用 `ClassPathBeanDefinitionScanner`?** + - 在 Spring Boot 应用程序中,通常不需要显式使用 `ClassPathBeanDefinitionScanner`。Spring Boot 使用自动配置机制来扫描和注册组件。如果需要自定义组件扫描,可以在配置类中使用 `@ComponentScan` 注解来指定要扫描的包。 +9. **`ClassPathBeanDefinitionScanner` 是否支持排除特定注解?** + - 可以使用 `ClassPathBeanDefinitionScanner` 的过滤器来排除具有特定注解的类,以防止它们被扫描和注册为 Spring Bean。 \ No newline at end of file diff --git a/spring-beans/spring-bean-classPathBeanDefinitionScanner/pom.xml b/spring-beans/spring-bean-classPathBeanDefinitionScanner/pom.xml new file mode 100644 index 0000000..dcdd512 --- /dev/null +++ b/spring-beans/spring-bean-classPathBeanDefinitionScanner/pom.xml @@ -0,0 +1,14 @@ + + + + spring-beans + com.xcs.spring + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-bean-classPathBeanDefinitionScanner + + \ No newline at end of file diff --git a/spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/ClassPathBeanDefinitionScannerDemo.java b/spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/ClassPathBeanDefinitionScannerDemo.java new file mode 100644 index 0000000..a35116d --- /dev/null +++ b/spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/ClassPathBeanDefinitionScannerDemo.java @@ -0,0 +1,29 @@ +package com.xcs.spring; + +import com.xcs.spring.controller.MyController; +import com.xcs.spring.repository.MyRepository; +import com.xcs.spring.service.MyService; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; + +/** + * @author xcs + * @date 2023年11月07日 17时45分 + **/ +public class ClassPathBeanDefinitionScannerDemo { + + public static void main(String[] args) { + // 创建一个 AnnotationConfigApplicationContext + DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); + + // 创建 ClassPathBeanDefinitionScanner 并将其关联到容器 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(factory); + + // 使用 ClassPathBeanDefinitionScanner的scan方法扫描Bean对象 + scanner.scan("com.xcs.spring"); + + System.out.println("MyController = " + factory.getBean(MyController.class)); + System.out.println("MyService = " + factory.getBean(MyService.class)); + System.out.println("MyRepository = " + factory.getBean(MyRepository.class)); + } +} diff --git a/spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/controller/MyController.java b/spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/controller/MyController.java new file mode 100644 index 0000000..1e08b24 --- /dev/null +++ b/spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/controller/MyController.java @@ -0,0 +1,11 @@ +package com.xcs.spring.controller; + +import org.springframework.stereotype.Controller; + +/** + * @author xcs + * @date 2023年11月07日 17时47分 + **/ +@Controller +public class MyController { +} diff --git a/spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/repository/MyRepository.java b/spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/repository/MyRepository.java new file mode 100644 index 0000000..ec8ba76 --- /dev/null +++ b/spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/repository/MyRepository.java @@ -0,0 +1,11 @@ +package com.xcs.spring.repository; + +import org.springframework.stereotype.Repository; + +/** + * @author xcs + * @date 2023年11月07日 17时46分 + **/ +@Repository +public class MyRepository { +} diff --git a/spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/service/MyService.java b/spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/service/MyService.java new file mode 100644 index 0000000..5c9b22f --- /dev/null +++ b/spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/service/MyService.java @@ -0,0 +1,11 @@ +package com.xcs.spring.service; + +import org.springframework.stereotype.Service; + +/** + * @author xcs + * @date 2023年11月07日 17时46分 + **/ +@Service +public class MyService { +}