本文是《BeanDefinition扫描过程源码解析》解析的前置补充文章。
一、基本概述
@ComponentScan 是 Spring 框架中用于自动扫描和注册 Bean 的核心注解。它告诉 Spring 容器在哪些包路径下查找带有 @Component、@Service、@Repository、@Controller 等注解的类,并将它们注册为 Spring Bean。
二、属性详解
1. 基础扫描路径配置
value / basePackages
// 两种写法等价
@ComponentScan("com.example")
@ComponentScan(basePackages = "com.example")
@ComponentScan(basePackages = {"com.example.dao", "com.example.service"})类型:
String[]作用:指定要扫描的基础包路径
注意:
value是basePackages的别名,两者功能完全相同默认值:空数组,如果不指定,则扫描声明该注解的类所在的包及其子包
basePackageClasses
@ComponentScan(basePackageClasses = {UserService.class, UserDao.class})类型:
Class<?>[]作用:通过类来间接指定包路径,Spring 会扫描这些类所在的包
优点:类型安全,重构时IDE会自动更新
典型用法:在包中创建一个标记接口(Marker Interface)
// 创建标记接口
package com.example.config;
public interface ScanMarker {}
// 使用标记接口
@ComponentScan(basePackageClasses = ScanMarker.class)
// 这样会扫描 com.example.config 包及其子包注意:只是说扫描,并没有说一定是Bean,Bean最终默认还是要标注@Component或者其衍生注解。
2. 包含/排除过滤器
includeFilters
使用语法如下:
@ComponentScan(
includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = MyCustomAnnotation.class),
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = MyBaseClass.class),
@Filter(type = FilterType.ASPECTJ, pattern = "com.example..*Service"),
@Filter(type = FilterType.REGEX, pattern = ".*Repository"),
@Filter(type = FilterType.CUSTOM, classes = MyCustomFilter.class)
}
)类型:
Filter[]作用:指定哪些组件应该被包含进来(即使它们没有
@Component等注解)与
useDefaultFilters的关系:当useDefaultFilters=true(默认)时,包含过滤器额外添加扫描目标
下面分别演示各种情况:
① 指定注解
@ComponentScan(
includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = MyCustomAnnotation.class),
}, useDefaultFilters = false
)
public class MyConfig {
}//@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME) // 注意:这个是必须要指定的,这样才能保证编译后的class文件有注解信息,Spring才能解析到
public @interface MyCustomAnnotation {
}public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
applicationContext.getBean("userService");
applicationContext.getBean("userDao");
applicationContext.getBean("userController");
}
}这里我把useDefaultFilters = false,并且三个业务类上只有@MyCustomAnnotation注解,也可以获取Bean。
② 指定类型
@ComponentScan(
useDefaultFilters = false,
includeFilters = {
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = UserBase.class)
}
)
public class MyConfig {
}public class UserBase {
}public class UserService extends UserBase {
}public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
applicationContext.getBean("userService");
applicationContext.getBean("userBase");
}
}这里我把useDefaultFilters = false,使用ASSIGNABLE_TYPE过滤器后基类与子类都没有加@Component注解,但都可以获取到Bean。
excludeFilters
使用语法如下:
@ComponentScan(
excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = Controller.class),
@Filter(type = FilterType.REGEX, pattern = ".*Test.*")
}
)类型:
Filter[]作用:排除特定的组件,即使它们匹配了包含条件或有
@Component注解
一般排除的优先级会高于包含
useDefaultFilters
@ComponentScan(useDefaultFilters = false)类型:
boolean作用:是否启用默认的过滤器
默认值:
true详细说明:
true(默认):会自动检测带有@Component、@Repository、@Service、@Controller、@Configuration的类false:关闭默认检测,只扫描includeFilters指定的组件
3. 作用域相关
scopedProxy
语法如下:
@ComponentScan(scopedProxy = ScopedProxyMode.INTERFACES)
@ComponentScan(scopedProxy = ScopedProxyMode.TARGET_CLASS)
@ComponentScan(scopedProxy = ScopedProxyMode.NO)类型:
ScopedProxyMode作用:为作用域 Bean 生成代理的方式
可选值:
ScopedProxyMode.NO(默认):不创建代理ScopedProxyMode.INTERFACES:使用 JDK 动态代理(基于接口)ScopedProxyMode.TARGET_CLASS:使用 CGLIB 代理(基于类)
典型场景:在单例 Bean 中注入 request/session 作用域的 Bean 时需要使用
这里只是先了解一下这个属性,后面会有地方详细学习的:单例 Bean 注入 request Bean 会失败,因为 request Bean 依赖 Web 请求上下文,而单例在容器启动时就创建了。Spring 的解决办法:使用代理(Scoped Proxy)来延迟真正对象的获取
scopeResolver
语法:
@ComponentScan(scopeResolver = MyCustomScopeResolver.class)类型:
Class<? extends ScopeMetadataResolver>作用:自定义作用域解析策略
默认值:
AnnotationScopeMetadataResolver.class
scopedProxyMode
类型:
ScopedProxyMode注意:Spring 4.3+ 已废弃,建议使用
scopedProxy属性
在 Spring Framework 6.x(从 6.0 开始)中,
scopedProxyMode已经被彻底移除,不再出现在源码里。
4. Bean 命名策略
nameGenerator
语法:
@ComponentScan(nameGenerator = MyBeanNameGenerator.class)类型:
Class<? extends BeanNameGenerator>作用:自定义 Bean 名称生成器
默认值:
AnnotationBeanNameGenerator.class自定义示例:
public class CustomNameGenerator extends AnnotationBeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
// 自定义命名逻辑
return "custom_" + super.generateBeanName(definition, registry);
}
}resourcePattern
语法:
@ComponentScan(resourcePattern = "**/*.class")类型:
String作用:控制扫描的资源模式
默认值:
"**/*.class"(扫描所有 class 文件)修改示例:
"**/*Controller.class"只扫描 Controller 类
5. 懒加载控制
lazyInit
语法:
@ComponentScan(lazyInit = true)类型:
boolean作用:是否懒初始化扫描到的 Bean
默认值:
false注意:这个设置会被
@Lazy注解覆盖
相当于是一个全局的控制,但是单个Bean定义可以覆盖
三、FilterType 详解
FilterType 的 5 种类型
1. ANNOTATION(注解过滤)
@Filter(type = FilterType.ANNOTATION, classes = {Service.class, Repository.class})最常用:按注解类型过滤
2. ASSIGNABLE_TYPE(类型过滤)
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BaseService.class, BaseDao.class})s扫描指定类及其子类
3. ASPECTJ(AspectJ 表达式)
@Filter(type = FilterType.ASPECTJ, pattern = "com.example.service.*Service")4. REGEX(正则表达式)
@Filter(type = FilterType.REGEX, pattern = ".*Controller")5. CUSTOM(自定义过滤)
@Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class)需要实现
TypeFilter接口:
public class MyTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) {
// 自定义匹配逻辑
return metadataReader.getClassMetadata().getClassName().contains("Service");
}
}