一、项目的结构如下:
二、使用 @ComponentScan 注解时,如果不给这个注解的任意属性赋值,那么该注解默认的扫描范围是什么?
1、在 com.spring01.config 包下创建两个类 SpringConfiguration、Animal.并且在 UserServiceImpl 上加上注解 @Service ,在 UserDaoImpl 上加上注解 @Repository
// 标记这是一个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);
}
}
@Component
public class Animal {
}
@Service
public class UserServiceImpl implements UserService {
}
@Repository
public class UserDaoImpl implements UserDao {
}
2、测试类
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、测试结果
// 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 注解源码如下:
@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 属性
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
value 和 basePackages 两个属性互为别名,也就是说无论使用它们中的哪一个属性效果都是一样的,它们的作用就是指定 @ComponentScan 这个注解的扫描范围
一般来说 value 和 basePackages 两个属性我们只会给其中的一个赋值,如果你要同时使用这两个属性,那么请保持这两个属性的值是一样的,否则会出现如下错误:
// 错误信息
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 属性
Filter[] excludeFilters() default {};
excludeFilters 的属性值是一个 Filter 类型的数组,我们可以看一下这个数组里面的取值
@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
@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);
}
}
测试结果:
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 这个接口
/** Filter candidates using a given custom
* {@link org.springframework.core.type.filter.TypeFilter} implementation.
*/
CUSTOM
自定义一个类 MyCustomTypeFilter 实现 TypeFilter 接口
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;
}
}
配置类
@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);
}
}
测试结果
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 注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 可重复注解
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
例如:
@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);
}
}