Spring 注解之 @ComponentScan
一、项目的结构如下:
二、使用 @ComponentScan 注解时,如果不给这个注解的任意属性赋值,那么该注解默认的扫描范围是什么?
1、在 com.spring01.config 包下创建两个类 SpringConfiguration、Animal.并且在 UserServiceImpl 上加上注解 @Service ,在 UserDaoImpl 上加上注解 @Repository
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // 标记这是一个Spring的配置类 @Configuration @ComponentScan public class SpringConfiguration { // @Bean 注解用于将 Person 类型的对象注入到 Spring 容器中,注入到容器中的对象类型为方法的返回值类型,默认注入的 id 是方法名 // 如果想指定注入到 Spring 容器中的 id ,可以使用 @Bean("xiaomaomao") ,代表注入到 Spring 容器的 id="xiaomaomao" @Bean public Person person01() { return new Person( "Bill Gates" , 66 ); } // 这里就代表往 spring 容器中注入了一个 Person 类型的对象, id 为 person02 @Bean public Person person02() { return new Person( "Linus Torvalds" , 44 ); } } |
1 2 3 | @Component public class Animal { } |
1 2 3 | @Service public class UserServiceImpl implements UserService { } |
1 2 3 | @Repository public class UserDaoImpl implements UserDao { } |
2、测试类
1 2 3 4 5 6 7 8 9 10 11 12 | public class SpringDemo { @Test public void springTest01() { ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration. class ); // 获取并打印所有的 Bean 的名称 String[] beanDefinitionNames = context.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } } } |
3、测试结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // Spring框架自身类 org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory // @ComponentScan 注解扫描,注入到 Spring 容器中,并交由 Spring 容器进行管理 // SpringConfiguration上标注的是 @Configuration,该注解实际上底层使用的是 @Component 注解,默认的 id 是类名的首字母小写 springConfiguration // Animal 上标注的是 @Component 注解,注入到 Spring 容器的时候默认的 id 是类名首字母小写 animal // @Bean注解,没有显示给值,id 是方法名 person01 person02 |
结果分析:除了框架自身的类之外,我们实际上注入到 Spring 容器中的对象有 springConfiguration、animal、person01、person02,而使用 @Service 和 @Repository 标注的注解却没有被扫描到并注入到 Spring 容器中,所以说单纯的使用 @ComponentScan 注解标注,而不给该注解的任何属性赋值,那么该注解的扫描范围就是配置类所在的包下面的所有类(配置类是SpringConfiguration ,其所在的包是 com.spring01.config ),而 UserServiceImpl 和 UserDaoImpl 虽然被 @Service 和 @Repository 注解标注,但是它们并不在配置类所在的包下,所以它们没有被扫描到.
总结: @ComponentScan 默认的扫描范围就是当前类所在的包下的所有注解.
三、使用 @ComponentScan 注解时,它有哪些属性,这些属性可以赋哪一些值?
@ComponentScan 注解源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | @Retention (RetentionPolicy.RUNTIME) @Target (ElementType.TYPE) @Documented @Repeatable (ComponentScans. class ) public @interface ComponentScan { @AliasFor ( "basePackages" ) String[] value() default {}; @AliasFor ( "value" ) String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator. class ; Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver. class ; ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT; String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN; boolean useDefaultFilters() default true ; Filter[] includeFilters() default {}; Filter[] excludeFilters() default {}; boolean lazyInit() default false ; @Retention (RetentionPolicy.RUNTIME) @Target ({}) @interface Filter { FilterType type() default FilterType.ANNOTATION; @AliasFor ( "classes" ) Class<?>[] value() default {}; @AliasFor ( "value" ) Class<?>[] classes() default {}; String[] pattern() default {}; } } |
下面我们就挑几个常用的属性来说一说吧
1、value / basePackages 属性
1 2 3 4 5 | @AliasFor ( "basePackages" ) String[] value() default {}; @AliasFor ( "value" ) String[] basePackages() default {}; |
value 和 basePackages 两个属性互为别名,也就是说无论使用它们中的哪一个属性效果都是一样的,它们的作用就是指定 @ComponentScan 这个注解的扫描范围
一般来说 value 和 basePackages 两个属性我们只会给其中的一个赋值,如果你要同时使用这两个属性,那么请保持这两个属性的值是一样的,否则会出现如下错误:
1 2 3 4 | // 错误信息 org.springframework.core.annotation.AnnotationConfigurationException: Different @AliasFor mirror values for annotation [org.springframework.context.annotation.ComponentScan] declared on class com.spring01.config.SpringConfiguration; attribute 'basePackages' and its alias 'value' are declared with values of [{com.spring01.dao}] and [{com.spring01}]. |
举例: @ComponentScan(value="com.spring01") / @ComponentScan(basePackages="com.spring01")
它们代表的意思都是扫描 com.spring01 包下的所有注解
2、excludeFilters 属性
1 | Filter[] excludeFilters() default {}; |
excludeFilters 的属性值是一个 Filter 类型的数组,我们可以看一下这个数组里面的取值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @Retention (RetentionPolicy.RUNTIME) @Target ({}) @interface Filter { // type 属性的默认值是注解类型 FilterType type() default FilterType.ANNOTATION; // value 属性和 classes 属性互为别名,只需要写一个就可以了 @AliasFor ( "classes" ) Class<?>[] value() default {}; // 与 value 互为别名 @AliasFor ( "value" ) Class<?>[] classes() default {}; // 类型 String[] pattern() default {}; } |
type 属性的是一个枚举类,里面的取值如下:
FilterType.ANNOTATION(默认值)
FilterType.ASSIGNABLE_TYPE
FilterType.ASSIGNABLE_TYPE
FilterType.ASPECTJ
FilterType.REGEX(正则表达式)
FilterType.CUSTOM(自定义规则)
其中最经常使用的值就是 FilterType.ANNOTATION、FilterType.CUSTOM,下面我们来演示一下这两个值有什么用
一、FilterType.ANNOTATION
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @Configuration // 扫描 com.spring01 包下面的所有注解 @ComponentScan (value = "com.spring01" , // 如果使用的是 excludeFilters, useDefaultFilters 的默认值就是 true,这里可以省略不写 useDefaultFilters = true , // 排除的类型是注解类型,排除的注解名称是 Service (也就是排除 @Service 注解标注的类) excludeFilters = { @ComponentScan .Filter(type=FilterType.ANNOTATION,classes={Service. class }) } ) // 配置类 public class SpringConfiguration { @Bean public Person person01() { return new Person( "Bill Gates" , 66 ); } @Bean public Person person02() { return new Person( "Linus Torvalds" , 44 ); } } |
测试结果:
1 2 3 4 5 6 7 8 9 10 | org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory springConfiguration animal userDaoImpl person01 person02 |
结果分析:由于我们使用了排除规则,指明了排除 @Service 注解,所以 UserServiceImpl 这个类没有被扫描,也就没有注入到 Spring 容器中
二、FilterType.CUSTOM
枚举值上面的注释可以看出,我们需要实现 TypeFilter 这个接口
1 2 3 4 | /** Filter candidates using a given custom * {@link org.springframework.core.type.filter.TypeFilter} implementation. */ CUSTOM |
自定义一个类 MyCustomTypeFilter 实现 TypeFilter 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class MyCustomTypeFilter implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // 当前扫描到的注解的元数据 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); // 当前被扫描的类的绝对路径 Resource resource = metadataReader.getResource(); // 当前被扫描的类的元数据 ClassMetadata classMetadata = metadataReader.getClassMetadata(); // 获取被扫描的类的包类全名 String className = classMetadata.getClassName(); // 类名中包含"User"的类 if (className.contains( "User" )){ // 返回值为 true ,代表的就是要排除 return true ; } return false ; } } |
配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @Configuration // 扫描 com.spring01 包下面的所有注解 @ComponentScan (value = "com.xiaomaomao" , // 如果使用的是 excludeFilters, useDefaultFilters 的默认值就是 true,这里可以省略不写 useDefaultFilters = true , // 排除的类型是自定义类型,自定义规则在 MyCustomTypeFilte 类中给出 excludeFilters = { @ComponentScan .Filter(type=FilterType.CUSTOM,classes={MyCustomTypeFilter. class }) } ) // 配置类 public class SpringConfiguration { @Bean public Person person01() { return new Person( "Bill Gates" , 66 ); } @Bean public Person person02() { return new Person( "Linus Torvalds" , 44 ); } } |
测试结果
1 2 3 4 5 6 7 8 9 | org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory springConfiguration animal person01 person02 |
结果分析:类名中包含 "User" 字符串的类都被排除了,最终注入 Spring 容器的类就没有 UserServiceImpl 和 UserDaoImpl
注意:排除规则只能排除配置类之外的其它类,配置类里面相应的信息是不能被排除的.
3、includeFilters属性
Filter[] includeFilters() default {},代表只扫描哪一些类或者是注解(取决于 type 的类型)用法和 excludeFilter 类似,唯一的区别就是,如果你想使用 includeFilter 进行筛选的时候,切记一定要将 useDefaultFilter 这个属性的值设置为 false.
4、@ComponentScan 注解是一个可重复注解,我们可以同时定义多个 @Component 注解
1 2 3 4 5 6 | @Retention (RetentionPolicy.RUNTIME) @Target (ElementType.TYPE) @Documented // 可重复注解 @Repeatable (ComponentScans. class ) public @interface ComponentScan { |
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | @Configuration @ComponentScans ({ @ComponentScan (value = "com.spring01" , useDefaultFilters = false , // 只扫描类名中包含 "Dao" includeFilters = { @ComponentScan .Filter(type=FilterType.CUSTOM,classes={MyCustomTypeFilter. class }) } ), @ComponentScan (value = "com.spring01" , useDefaultFilters = false , // 排除 @Service 注解 excludeFilters = { @ComponentScan .Filter(type=FilterType.ANNOTATION,classes={Service. class }) } ) } ) @ComponentScan (value = "com.spring01" , // 如果使用的是 excludeFilters, useDefaultFilters的默认值就是 true,这里可以省略不写 useDefaultFilters = false , // 使用自定义的TypeFilter includeFilters = { @ComponentScan .Filter(type=FilterType.CUSTOM,classes={MyCustomTypeFilter. class }) } ) public class SpringConfiguration { @Bean public Person person01() { return new Person( "Bill Gates" , 66 ); } @Bean public Person person02() { return new Person( "Linus Torvalds" , 44 ); } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?