Spring 注解开发之 @ComponentScan

时间:2022-07-22
本文章向大家介绍Spring 注解开发之 @ComponentScan,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

这次介绍一下 Spring 中比较重要的一个注解 @ComponentScan

本文的组织结构如下:

  • 先看一下该注解取代了配置文件中的哪些配置;
  • 再总览该注解有哪些属性值;
  • 最后讲解一下重要的属性值。

Spring 版本 5.1.2.RELEASE

一、XML 配置

@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 {};

1、Filter 子注解

该属性值需要一个 Filter 数组,而 FilterComponentScan 的一个内部类,是一个子注解。

该注解的基本使用如下:

@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {
	Controller.class, Service.class
})

该注解有两个属性值比较重要:

  • type:规定使用哪一种模式过滤掉类;
  • value:给出具体的值,注解就给类型,正则就给匹配规则等等······

拆解上面的例子分析:

泛型 FilterType 规定几种候选的模式,默认是 ANNOTATION 即根据注解排除:

FilterType type() default FilterType.ANNOTATION;

属性 classesvalue ,根据前面规定的模式传值:

@AliasFor("classes")
Class<?>[] value() default {};

2、FilterType 泛型

type 取值的泛型类 FilterType 有以下几种取值:

  • ANNOTATION:使用注解过滤;
  • ASSIGNABLE_TYPE:使用给定的类型;
  • ASPECTJ:使用 ASPECTJ 表达式;
  • REGEX:使用正则表达式;
  • CUSTOM:使用自定义规则,需要提供一个 TypeFilter 的实现类;

3、自定义过滤规则

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);
}