这次介绍一下 Spring
中比较重要的一个注解 @ComponentScan
。
本文的组织结构如下:
Spring 版本 5.1.2.RELEASE
@ComponentScan
注解取代了配置文件中的如下配置:
<context:component-scan base-package="top.wsuo"/>
这一行配置的意思是开启包扫描,会自动扫描带有 @component
及其 衍生注解 的类:,将其放入容器中。
该配置还有一个属性:use-default-filters
,将其设为 false
即代表不使用默认的 include
规则,而使用自己规定的规则。该配置对应于@ComponentScan
的同名属性值。
来看一下下面这个注解:
@ComponentScan(
value = "top.wsuo",
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {
Controller.class, Service.class
})
},
includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {
Repository.class
})
},
useDefaultFilters = false
)
value
指定要扫描的包路径;excludeFilters
指定排除的规则;includeFilters
指定包含的规则,要想该属性生效需设置 useDefaultFilters = false
;重要属性是 excludeFilters
以及 includeFilters
,两属性类似,这里以前者为例:
/**
* Specifies which types are not eligible for component scanning.
* @see #resourcePattern
*/
Filter[] excludeFilters() default {};
该属性值需要一个 Filter
数组,而 Filter
是 ComponentScan
的一个内部类,是一个子注解。
该注解的基本使用如下:
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {
Controller.class, Service.class
})
该注解有两个属性值比较重要:
type
:规定使用哪一种模式过滤掉类;value
:给出具体的值,注解就给类型,正则就给匹配规则等等······拆解上面的例子分析:
泛型 FilterType
规定几种候选的模式,默认是 ANNOTATION
即根据注解排除:
FilterType type() default FilterType.ANNOTATION;
属性 classes
即 value
,根据前面规定的模式传值:
@AliasFor("classes")
Class<?>[] value() default {};
type
取值的泛型类 FilterType
有以下几种取值:
ANNOTATION
:使用注解过滤;ASSIGNABLE_TYPE
:使用给定的类型;ASPECTJ
:使用 ASPECTJ 表达式;REGEX
:使用正则表达式;CUSTOM
:使用自定义规则,需要提供一个 TypeFilter
的实现类;TypeFilter
是一个接口,需要我们提供一个实现类,实现一个方法:
/**
* 设置匹配规则
*
* @param metadataReader 读取当前正在扫描的类的信元数据
* @param metadataReaderFactory 可以获取其他任何类的元数据
* @return 返回匹配情况
* @throws IOException IO异常
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException
该方法有两个参数:
metadataReader
:读取当前正在扫描的类的信元数据;metadataReaderFactory
:可以获取其他任何类的元数据。其中 metadataReader
对象有下面几个常用的方法:
// 获取当前类的注解元数据
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取当前正在扫描的类的元数据
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
我们可以通过获取正在扫描的类来决定是加入到容器中还是不加入。
private static final String RULES = "er";
/**
* 设置匹配规则
*
* @param metadataReader 读取当前正在扫描的类的信元数据
* @param metadataReaderFactory 可以获取其他任何类的元数据
* @return 返回匹配情况
* @throws IOException IO异常
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取当前类的注解元数据
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取当前正在扫描的类的元数据
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
// 获取类名
String className = classMetadata.getClassName();
// 如果类名包含 er 就加入到容器中
return className.contains(RULES);
}