logo头像

Always believe youself.

ComponentScan自动扫描组件

ComponentScan

创建一个配置类,在配置类上添加 @ComponentScan 注解。该注解默认会扫描该类所在的包下所有的配置类,相当于之前的 <context:component-scan>

import org.springframework.context.annotation.ComponentScan;

@ComponentScan
public class BeanConfig {

}

使用 ApplicationContext 的 getBeanDefinitionNames() 方法获取已经注册到容器中的 bean 的名称。

为什么 @Service 注解的类也被注册了呢?

@ComponentScan 的一个 useDefaultFilters 属性的用法,该属性默认值为 true。

spring 默认会自动发现被 @Component、@Repository、@Service 和 @Controller 标注的类,并注册进容器中。要达到只包含某些包的扫描效果,就必须将这个默认行为给禁用掉(在 @ComponentScan 中将 useDefaultFilters 设为 false 即可)。

添加多种扫描规则

  • 如果使用的 jdk8,则可以直接添加多个 @ComponentScan 来添加多个扫描规则,但是在配置类中要加上 @Configuration 注解,否则无效。

    package io.mieux.config;

    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;

    @ComponentScan(value = “io.mieux.controller”)
    @ComponentScan(value = “io.mieux.service”)
    @Configuration
    public class BeanConfig {

    }

  • 可以使用 @ComponentScans 来添加多个 @ComponentScan,从而实现添加多个扫描规则。同样,也需要加上 @Configuration 注解,否则无效。

    package io.mieux.config;

    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.ComponentScans;
    import org.springframework.context.annotation.Configuration;

    @ComponentScans(value =

    {@ComponentScan(value = "io.mieux.controller"),
    @ComponentScan(value = "io.mieux.service")})
    

    @Configuration
    public class BeanConfig {

    }

添加自定义过滤规则

在前面使用过 @Filter 注解,里面的 type 属性是一个 FilterType 的枚举类型:

public enum FilterType {

    ANNOTATION,
    ASSIGNABLE_TYPE,
    ASPECTJ,
    REGEX,
    CUSTOM

}

使用 CUSTOM 类型,就可以实现自定义过滤规则

  • 首先创建一个实现 TypeFilter 接口的 CustomTypeFilter 类,并实现其 match 方法。

    package io.mieux.config;

    import org.springframework.core.io.Resource;
    import org.springframework.core.type.AnnotationMetadata;
    import org.springframework.core.type.ClassMetadata;
    import org.springframework.core.type.classreading.MetadataReader;
    import org.springframework.core.type.classreading.MetadataReaderFactory;
    import org.springframework.core.type.filter.TypeFilter;

    import java.io.IOException;

    public class CustomTypeFilter implements TypeFilter {

    @Override
    public boolean match(MetadataReader metadataReader,
                         MetadataReaderFactory metadataReaderFactory) throws IOException {
    
        // 获取当前扫描到的类的注解元数据
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        // 获取当前扫描到的类的元数据
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        // 获取当前扫描到的类的资源信息
        Resource resource = metadataReader.getResource();
    
        if (classMetadata.getClassName().contains("Co")) {
            return true;
        }
    
        return false;
    }
    

    }

这里简单对扫描到的类名进行判断,如果类名包含”Co“的就符合条件,也就会注入到容器中。

  • 对 BeanConfig 进行修改,指定过滤类型为 Custom 类型,并指定 value 为 CustomTypeFilter.class。

    package io.mieux.config;

    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.FilterType;

    @ComponentScan(value = “io.mieux”,

    includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, value = {CustomTypeFilter.class})},
    useDefaultFilters = false)
    

    public class BeanConfig {
    }

探索源码

只要注解里面含有@Component,就会被Spring扫描,并注册。为什么会这么呢?

实现的match()方法返回true。从方法名猜测只要类中包含注解@Component或者@ManageBean就会匹配成功。下面继续深入源码分析。

只有两个方法:

  • metadata.hasAnnotation(this.annotationType.getName()),这是判断类上是否含有给定的注解,
  • metadata.hasMetaAnnotation(this.annotationType.getName()),这是判断底层class是否含有给定的注解,而@StringRepository的底层确实包含了@Component,所以这里方法返回true。

原理

AnnotationTypeFilter#match -> matchSelf

protected boolean matchSelf(MetadataReader metadataReader) {
    AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();    // #1
    return metadata.hasAnnotation(this.annotationType.getName()) ||    // #2
            (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));    // #3
}
  • 获取Class的注解元数据
  • 检查Class上是否有对应的annotationType
  • 检查Class的嵌套注解是否有对应的annotationType

@Service,@Repository,@Controller注解上都标注了@Component注解,如果Class上使用了这些注解,3步骤是返回true的

简单来说,Spring扫描对应目录下的class,生成BeanDefinition并注册到Spring上下文。最后构造bean的操作,是在AbstractApplicationContext#refresh方法中,调用finishBeanFactoryInitialization,构建热加载的单例bean时完成。

ComponentScan 常用的属性

  • String[] value() default{}; // 指定包扫描路径
  • Class<?>[] basePackagesClasses() default{}; // 扫描具体的类,可以支持同时指定多个类。
  • boolean useDefaultFilters() default true; // 是否对含以下类开启检测,默认是开启的 @Component @Repository @Service @controller
  • 指定某些 Filter 扫描的类, 可以自定义
  • 排查过滤器扫描的类。

@Component 使用此注解会被Spring加载到IOC容器中,@ComponentScan 扫描带 @Component 注解;注解的派生性 @Service

详情可以看 : https://segmentfault.com/a/1190000022710818?utm_source=tag-newest