@ComponentScan注解的介绍
这个注解是作用在spring的配置类当中的,用于扫描当前配置类所在的包下的所有类,当然我们也可以指定扫描哪些包,或者指定排除掉哪些包
该注解包含的参数
我们从代码中进入这个注解的定义可以看到如下代码
@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 {};
}
}
我们挑出上面比较重要的几个参数来说明一下
value:指定要扫描的包
excludeFilters = Filter[]:指定要排除那些包
type:根据什么来排除
FilterType.ANNOTATION:根据注解排除
FilterType.ASSIGNABLE_TYPE:根据类型排除
FilterType.ASPECTJ:根据表达式排除
FilterType.REGEX:根据正则表达式排除
FilterType.CUSTOM:自定义排除规则
classes:只需要写对应排除类型的class
includeFilters = Filter[]:指定要包含那些包
type:根据什么来包含
FilterType.ANNOTATION:根据注解包含
FilterType.ASSIGNABLE_TYPE:根据类型包含
FilterType.ASPECTJ:根据表达式包含
FilterType.REGEX:根据正则表达式包含
FilterType.CUSTOM:自定义包含规则
classes:只需要写对应包含类型的class
useDefaultFilters:是否默认扫描所有包
以上就是该注解参数的详细描述
@ComponentScan注解的使用
使用范例
范例一
直接拿根据注解扫描对应包下的class的范例来演示
- 首先建立一个配置类
@Configuration
@ComponentScan(value = "com.ioc", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
},useDefaultFilters = false)
public class MainConfig02 {
}
分析一下上面配置类的注解@ComponentScan,我们可以看到value代表着我们要扫描的是com.ioc包下的类,同时从includeFilters 可以看出我们包含
的规则是根据注解
来扫描类的,对于根据哪些注解,我们可以冲classes中看到有以下两个注解:@Controller @Service.
最后我们把useDefaultFilters 设置为false不扫描所有包
然后为了让我们的配置类能够扫描到有指定注解的类,我们先建立两个分别带有@Controller @Service的类
- 建立能够被扫描到的类
package com.ioc.controller;
import org.springframework.stereotype.Controller;
/**
* Copyright (C), 2018-2021
* FileName: PersonController
* Author: BurNIng
* Date: 2021/11/5 15:49
* Description: 测试用的controller类
*/
@Controller
public class PersonController {
}
package com.ioc.service;
import org.springframework.stereotype.Service;
/**
* Copyright (C), 2018-2021
* FileName: PersonService
* Author: BurNIng
* Date: 2021/11/5 15:50
* Description: 测试用的service类
*/
@Service
public class PersonService {
}
这两个类都带有注解,然后我们来测试以下是否能够扫描并注入这些class
- 编写测试类获取被扫描到的bean
public class Test02 {
public static void main(String[] args) {
//获取容器
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MainConfig02.class);
//输出容器中bean的名称
for (String beanDefinitionName : annotationConfigApplicationContext.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
}
}
运行之后得到的结果如下
可以看到后面两个就是我们需要注入的class了
范例二
同样的我们把配置类上面的注解稍作修改,用另一种方式来扫描class
- 编写配置类
@ComponentScan(value = "com.ioc", includeFilters = {
@Filter(type = FilterType.CUSTOM, classes = {MyFilterType.class})
},useDefaultFilters = false)
public class MainConfig02 {
}
可以看到我们把type改为了CUSTOM
自定义的扫描规则,自定义的逻辑都写在MyFilterType
这个类中.那么我们看看这个类需要怎样编写.
- 编写自定义过滤规则类
/**
* Copyright (C), 2018-2021
* FileName: MyFilterType
* Author: BurNIng
* Date: 2021/11/5 16:37
* Description: 自定义规则扫描包含或者排除
*/
public class MyFilterType implements TypeFilter {
/**
* 确定此过滤器是否与给定元数据描述的类匹配
* @param metadataReader 读取到的当前正在扫描的类的信息
* @param metadataReaderFactory 可以获取到其他任何类的信息
* @return 此过滤器是否匹配
* @throws IOException
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前扫描类的注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前扫描类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类的资源信息(比如类路径)
Resource resource = metadataReader.getResource();
//如果是包含规则,那就把类名包含er的类全部全部扫描到位
if(classMetadata.getClassName().contains("er")) {
return true;
}
return false;
}
}
以上的参数信息在注释中解释的很详细了,重点介绍下运行过程.首先扫描到的类都会经过这个match方法,根据我对源码的跟踪
可以看到上方的for循环获取到了扫描到的所有类资源进行遍历,为了证明Resource就是类的资源信息,我特地翻译了一下
然后循环调用isCandidateComponent方法,正是这个方法调用了match
这个方法分为排除的情况调用match方法和包含情况的调用match方法,传入对应的参数,然后我们重写这个方法,利用这两个参数可以获取类的信息,这样就可以判断该类是否可以被注入到容器中,可以看到match方法返回true时,当前的class就可以被注入到容器中,为false时则忽略,在我们自定义的过滤规则类中,我们获取了类的名称,并让类名中含有er的类被注入到容器中.
- 测试类
public class Test02 {
public static void main(String[] args) {
//获取容器
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MainConfig02.class);
//输出容器中bean的名称
for (String beanDefinitionName : annotationConfigApplicationContext.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
}
}
和上面的测试类还是一样,最后的输出结果是
可以看到所有带有er的类名都被注入到容器中去了
以上我就列举了根据注解的方式排除或者包含的扫描方式和自定义的方式排除或者包含的扫描,希望大家有一定的收获