doScan 方法是 Spring 框架中用于扫描指定包路径并注册 Bean 定义的核心方法,通常在组件扫描(如 @ComponentScan)时触发。以下是逐行代码解析及其调用时机说明:
/**
* 扫描指定的基础包路径,查找所有候选组件(如标记了 @Component 的类),
* 并将它们注册为 Spring 容器中的 BeanDefinition。
* 此方法由 ClassPathBeanDefinitionScanner 的 scan() 方法调用,
* 通常在 Spring 容器启动时处理 @ComponentScan 或 XML 的 <context:component-scan> 时触发。
*/
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 校验基础包路径不能为空
Assert.notEmpty(basePackages, "At least one base package must be specified");
// 存储最终注册的 BeanDefinitionHolder(包含 BeanDefinition 和 BeanName)
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
// 遍历所有基础包路径
for (String basePackage : basePackages) {
// 1. 扫描包路径下的类文件,解析带有 @Component 的类,生成候选 BeanDefinition
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
// 遍历所有候选 BeanDefinition
for (BeanDefinition candidate : candidates) {
// 2. 解析 Bean 的作用域(如 @Scope("prototype"),默认 singleton)
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 3. 生成 Bean 的名称(默认使用类名首字母小写,或通过 @Bean 指定)
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
// 4. 后置处理 AbstractBeanDefinition(设置默认值,如懒加载、初始化方法等)
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
// 5. 处理通用注解(如 @Lazy、@Primary、@DependsOn)
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 6. 检查是否允许注册该 Bean(避免重复注册)
if (checkCandidate(beanName, candidate)) {
// 包装 BeanDefinition 和 BeanName
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
// 7. 处理作用域代理模式(如 @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS))
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(
scopeMetadata, definitionHolder, this.registry
);
// 将 BeanDefinitionHolder 加入结果集,以便后续使用
beanDefinitions.add(definitionHolder);
// 8. 注册 BeanDefinition 到容器(最终存入 DefaultListableBeanFactory 的 beanDefinitionMap)
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
1. 方法定义
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
作用:扫描指定的包路径,查找候选 Bean 并注册到容器。
参数:basePackages 表示要扫描的基础包路径(如 com.example.service)。
返回值:包含所有注册的 Bean 定义及其名称的集合(BeanDefinitionHolder)。
2. 参数校验
Assert.notEmpty(basePackages, "At least one base package must be specified");
作用:检查 basePackages 是否为空,若为空则抛出异常。
意义:确保至少指定一个包路径,避免无效扫描。
3. 初始化结果集合
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
作用:使用 LinkedHashSet 存储 Bean 定义,保证插入顺序。
4. 遍历所有基础包
for (String basePackage : basePackages) {
作用:逐个处理每个包路径,确保所有指定包都被扫描。
5. 查找候选 Bean 定义
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
作用:调用 findCandidateComponents 扫描包路径下的类文件,识别带有注解(如 @Component)的类,生成 BeanDefinition。
底层实现:通过类路径扫描(ClassPathScanning)实现,筛选候选 Bean。
6. 处理每个候选 Bean
for (BeanDefinition candidate : candidates) {
作用:逐个处理扫描到的候选 Bean。
7. 解析作用域元数据
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
作用:解析 Bean 的作用域(如 singleton、prototype),默认通过 @Scope 注解配置。
示例:若类上有 @Scope(“prototype”),则设置作用域为 prototype。
8. 生成 Bean 名称
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
作用:生成 Bean 的唯一名称,默认规则是类名首字母小写(如 userService)。
扩展:可自定义 BeanNameGenerator 实现个性化命名。
9. 后处理 Bean 定义
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
作用:对 AbstractBeanDefinition 进行后处理,设置默认属性(如懒加载、初始化方法)。
典型操作:若未显式配置 init-method,可能在此处设置默认值。
10. 处理通用注解
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
作用:解析类上的通用注解(如 @Lazy、@Primary、@DependsOn),将其配置应用到 Bean 定义。
示例:@Lazy 会设置 Bean 的懒加载属性为 true。
11. 检查候选 Bean 是否可注册
if (checkCandidate(beanName, candidate)) {
作用:检查容器中是否已存在同名 Bean,根据策略决定是否覆盖或忽略。
策略:若已存在同名 Bean 且不允许覆盖,则跳过注册。
12. 创建 Bean 定义持有者
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
作用:将 Bean 定义与名称封装为 BeanDefinitionHolder,方便后续处理。
13. 应用作用域代理模式
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
作用:根据作用域(如 request、session)创建代理对象,确保作用域行为正确。
示例:对于 request 作用域的 Bean,生成代理以保证每次 HTTP 请求返回新实例。
14. 注册 Bean 定义
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
作用:将 BeanDefinitionHolder 添加到结果集合并注册到容器(如 DefaultListableBeanFactory)。
注册逻辑:调用 BeanDefinitionRegistry.registerBeanDefinition() 完成注册。
15. 返回结果集合
return beanDefinitions;
作用:返回所有成功注册的 Bean 定义。
doScan 方法调用时机
- 组件扫描触发
场景:当使用 @ComponentScan 注解或在 XML 中配置 context:component-scan 时。
流程:Spring 容器启动时,ComponentScanBeanDefinitionParser 解析配置,最终调用 ClassPathBeanDefinitionScanner.scan(),进而触发 doScan。 - 自动配置阶段
场景:Spring Boot 的 @SpringBootApplication 隐含 @ComponentScan,启动时自动扫描主类所在包及其子包。
流程:在 refreshContext() 阶段处理 BeanDefinitionRegistryPostProcessor,触发扫描。 - 自定义扫描逻辑
场景:通过编程方式调用扫描(如 new ClassPathBeanDefinitionScanner(registry).scan(“com.example”))。
示例:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.example");
context.refresh();
总结
代码作用:doScan 是 Spring 组件扫描的核心方法,负责扫描包路径、解析 Bean 元数据、生成名称、处理注解,并最终注册 Bean 定义。
调用时机:在 Spring 容器初始化阶段,由组件扫描配置触发(如 @ComponentScan),或在编程式扫描时显式调用。