参考文献

@ComponentScan

  • @ComponentScan用于批量注册Bean.这个注解会让Spring去扫描某些包及其子包中所有的类.然后将满足一定条件的类作为Bean注册到Spring容器中.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// org.springframework.context.annotation.ComponentScan
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

// 指定需要扫描的包
@AliasFor("basePackages")
String[] value() default {};

// 作用和value一样,value和basePackages不能同时存在的设置,可二选一
@AliasFor("value")
String[] basePackages() default {};

// 制定一些类,Spring容器会扫描这些类所在的包及其子包中的类
Class<?>[] basePackageClasses() default {};

// 自定义bean名称生成器
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

// 用于解析检测到的组件的作用域(Scope)
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

// 指示是否为检测到的组件生成代理,这在使用作用域时可能是必要的
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

// 需要扫描包中的哪些资源,默认为: **/**.class 即扫描指定包中所有的class文件
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;

// 对扫描的类是否启用默认过滤器,默认为true
boolean useDefaultFilters() default true;

// 过滤器,用来配置被扫描出来的哪些会被作为组件注册到容器中
Filter[] includeFilters() default {};

// 过滤器,用来排除哪些类,使其不会被注册容器中
Filter[] excludeFilters() default {};

// 是否延迟初始化被注册的Bean
boolean lazyInit() default false;

}

工作流程

  • Spring会扫描指定的包,且会递归下面子包,得到一批类的数组

  • 然后这些类会经过各种过滤器,最后剩下的类会被注册到容器中.

    • org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents真正执行过滤
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    // org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

    /**
    * Apply processing and build a complete {@link ConfigurationClass} by reading the
    * annotations, members and methods from the source class. This method can be called
    * multiple times as relevant sources are discovered.
    * @param configClass the configuration class being build
    * @param sourceClass a source class
    * @return the superclass, or {@code null} if none found or previously processed
    */
    @Nullable
    protected final SourceClass doProcessConfigurationClass(
    ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
    throws IOException {

    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
    // Recursively process any member (nested) classes first
    processMemberClasses(configClass, sourceClass, filter);
    }

    // Process any @PropertySource annotations
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
    sourceClass.getMetadata(), PropertySources.class,
    org.springframework.context.annotation.PropertySource.class)) {
    if (this.environment instanceof ConfigurableEnvironment) {
    processPropertySource(propertySource);
    }
    else {
    logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
    "]. Reason: Environment must implement ConfigurableEnvironment");
    }
    }

    // Process any @ComponentScan annotations
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
    sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
    !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
    for (AnnotationAttributes componentScan : componentScans) {
    // The config class is annotated with @ComponentScan -> perform the scan immediately
    Set<BeanDefinitionHolder> scannedBeanDefinitions =
    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
    // 对扫描出来的类进行过滤
    // Check the set of scanned definitions for any further config classes and parse recursively if needed
    for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
    BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
    if (bdCand == null) {
    bdCand = holder.getBeanDefinition();
    }
    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
    // 将扫描出来的类委托到parse方法中递归处理
    parse(bdCand.getBeanClassName(), holder.getBeanName());
    }
    }
    }
    }

    // Process any @Import annotations
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

    // Process any @ImportResource annotations
    AnnotationAttributes importResource =
    AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {
    String[] resources = importResource.getStringArray("locations");
    Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
    for (String resource : resources) {
    String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
    configClass.addImportedResource(resolvedResource, readerClass);
    }
    }

    // Process individual @Bean methods
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
    configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // Process default methods on interfaces
    processInterfaces(configClass, sourceClass);

    // Process superclass, if any
    if (sourceClass.getMetadata().hasSuperClass()) {
    String superclass = sourceClass.getMetadata().getSuperClassName();
    if (superclass != null && !superclass.startsWith("java") &&
    !this.knownSuperclasses.containsKey(superclass)) {
    this.knownSuperclasses.put(superclass, configClass);
    // Superclass found, return its annotation metadata and recurse
    return sourceClass.getSuperClass();
    }
    }

    // No superclass -> processing is complete
    return null;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    	// org.springframework.context.annotation.ComponentScanAnnotationParser#parse
    public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
    componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

    Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
    boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
    scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
    BeanUtils.instantiateClass(generatorClass));

    // scopedProxy属性构造
    ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
    if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
    scanner.setScopedProxyMode(scopedProxyMode);
    }
    else {
    Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
    scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
    }

    // resourcePattern属性构造
    scanner.setResourcePattern(componentScan.getString("resourcePattern"));

    // includeFilter属性构造
    for (AnnotationAttributes includeFilterAttributes : componentScan.getAnnotationArray("includeFilters")) {
    List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(includeFilterAttributes, this.environment,
    this.resourceLoader, this.registry);
    for (TypeFilter typeFilter : typeFilters) {
    scanner.addIncludeFilter(typeFilter);
    }
    }
    // excludeFilter属性构造
    for (AnnotationAttributes excludeFilterAttributes : componentScan.getAnnotationArray("excludeFilters")) {
    List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(excludeFilterAttributes, this.environment,
    this.resourceLoader, this.registry);
    for (TypeFilter typeFilter : typeFilters) {
    scanner.addExcludeFilter(typeFilter);
    }
    }

    boolean lazyInit = componentScan.getBoolean("lazyInit");
    if (lazyInit) {
    scanner.getBeanDefinitionDefaults().setLazyInit(true);
    }

    // basePackages设置
    Set<String> basePackages = new LinkedHashSet<>();
    String[] basePackagesArray = componentScan.getStringArray("basePackages");
    for (String pkg : basePackagesArray) {
    String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
    ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    Collections.addAll(basePackages, tokenized);
    }
    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
    basePackages.add(ClassUtils.getPackageName(clazz));
    }
    if (basePackages.isEmpty()) {
    basePackages.add(ClassUtils.getPackageName(declaringClass));
    }

    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
    @Override
    protected boolean matchClassName(String className) {
    return declaringClass.equals(className);
    }
    });
    return scanner.doScan(StringUtils.toStringArray(basePackages));
    }

注意事项

参数basePackage,basePackageClasses

  • 指定包名的方式扫描存在的一个隐患,若包被重名了,会导致扫描会失效,一般情况下面我们使用basePackageClasses的方式来指定需要扫描的包,这个参数可以指定一些类型,默认会扫描这些类所在的包及其子包中所有的类,这种方式可以有效避免这种问题

参数includeFilters

1
Filter[] includeFilters() default {};
  • 是一个Filter类型的数组,多个Filter之间为或者关系,即满足任意一个就可以了.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    @interface Filter {

    FilterType type() default FilterType.ANNOTATION;

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


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

    String[] pattern() default {};

    }
    • type:过滤器的类型,是个枚举类型,5种类型
      • ANNOTATION:通过注解的方式来筛选候选者,即判断候选者是否有指定的注解
      • ASSIGNABLE_TYPE:通过指定的类型来筛选候选者,即判断候选者是否是指定的类型
      • ASPECTJ:ASPECTJ表达式方式,即判断候选者是否匹配ASPECTJ表达式
      • REGEX:正则表达式方式,即判断候选者的完整名称是否和正则表达式匹配
      • CUSTOM:用户自定义过滤器来筛选候选者,对候选者的筛选交给用户自己来判断
    • classes: 3种情况如下
      • type=FilterType.ANNOTATION时,通过classes参数可以指定一些注解,用来判断被扫描的类上是否有classes参数指定的注解
      • type=FilterType.ASSIGNABLE_TYPE时,通过classes参数可以指定一些类型,用来判断被扫描的类是否是classes参数指定的类型
      • type=FilterType.CUSTOM时,表示这个过滤器是用户自定义的classes参数就是用来指定用户自定义的过滤器,自定义的过滤器需要实现org.springframework.core.type.filter.TypeFilter接口
    • pattern: 2种情况如下
      • type=FilterType.ASPECTJ时,通过pattern来指定需要匹配的ASPECTJ表达式的值
      • type=FilterType.REGEX时,通过pattern来自正则表达式的值

自定义Filter

实现步骤

  • 设置@Filtertype的类型为:FilterType.CUSTOM

  • 自定义过滤器类,需要实现接口:org.springframework.core.type.filter.TypeFilter

    1
    2
    3
    4
    5
    6
    7
    @FunctionalInterface
    public interface TypeFilter {

    boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
    throws IOException;

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class MyFilter implements TypeFilter {
       /**
        * @param metadataReader
        * @param metadataReaderFactory
        * @return
        * @throws IOException
        */
       @Override
       public boolean match(MetadataReader metadataReader, MetadataReaderFactory
    metadataReaderFactory) throws IOException {
           Class curClass = null;
           try {
               //当前被扫描的类
               curClass = Class.forName(metadataReader.getClassMetadata().getClassName());
          } catch (ClassNotFoundException e) {
               e.printStackTrace();
        }
          //判断curClass是否是IService类型
          boolean result = IService.class.isAssignableFrom(curClass);
          return result;
         }  
    }     
  • 设置@Filter中的classses为自定义的过滤器类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import org.springframework.context.annotation.ComponentScan; 
    import org.springframework.context.annotation.FilterType;

    @ComponentScan(
           basePackages = {"com.example.test"},
           useDefaultFilters = false, //不启用默认过滤器
           includeFilters = {
                   @ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyFilter.class)
          })
    public class ScanBean5 {
    }