Spring 包扫描注解 @ComponentScan

igxiaoshan Lv5

@ComponentScan使用场景

入门介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ==================== 源码 ====================
/**
* @since 3.1
* @see Configuration
*/
@Repeatable(ComponentScans.class)
public @interface ComponentScan {}


/**
* @since 4.3
* @see ComponentScan
*/
public @interface ComponentScans {}

通过源码可以看到@ComponentScan注解标记着 @since 3.1;说明 @ComponentScan 注解是从 3.1版本提供的;在@ComponentScan注解上标记了一个@Repeatbale注解,@Repeatable注解的属性值为ComponentScans.class.查看@ComponentScans注解源码,类上标记着@since 4.3,也就是说@ComponentSacns注解是从Spring 4.3版本开始提供的. @ComponentScans注解相当于是@ComponentScan注解的一个数组,@ComponentScans注解可以多次使用@ComponentScan注解来扫描不同的包路径

注解说明

@ComponentScan注解的核心功能就是 Spring IOC 容器再刷新的时候会扫描对应包下标注了@Component,@Configuration,@Repository,@Service,@Controller等注解的类,生成扫描到的Bean定义信息,将其保存到BeanFactorybeanDifinitionMap

源码解析

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// ==================== 全文源码 ====================
/**
* @author Chris Beams
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.1
* @see Configuration
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

/**
* Alias for {@link #basePackages}.
* <p>Allows for more concise annotation declarations if no other attributes
* are needed &mdash; for example, {@code @ComponentScan("org.my.pkg")}
* instead of {@code @ComponentScan(basePackages = "org.my.pkg")}.
*/
@AliasFor("basePackages")
String[] value() default {};

/**
* Base packages to scan for annotated components.
* <p>{@link #value} is an alias for (and mutually exclusive with) this
* attribute.
* <p>Use {@link #basePackageClasses} for a type-safe alternative to
* String-based package names.
*/
@AliasFor("value")
String[] basePackages() default {};

/**
* Type-safe alternative to {@link #basePackages} for specifying the packages
* to scan for annotated components. The package of each class specified will be scanned.
* <p>Consider creating a special no-op marker class or interface in each package
* that serves no purpose other than being referenced by this attribute.
*/
Class<?>[] basePackageClasses() default {};

/**
* The {@link BeanNameGenerator} class to be used for naming detected components
* within the Spring container.
* <p>The default value of the {@link BeanNameGenerator} interface itself indicates
* that the scanner used to process this {@code @ComponentScan} annotation should
* use its inherited bean name generator, e.g. the default
* {@link AnnotationBeanNameGenerator} or any custom instance supplied to the
* application context at bootstrap time.
* @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator)
* @see AnnotationBeanNameGenerator
* @see FullyQualifiedAnnotationBeanNameGenerator
*/
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

/**
* The {@link ScopeMetadataResolver} to be used for resolving the scope of detected components.
*/
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

/**
* Indicates whether proxies should be generated for detected components, which may be
* necessary when using scopes in a proxy-style fashion.
* <p>The default is defer to the default behavior of the component scanner used to
* execute the actual scan.
* <p>Note that setting this attribute overrides any value set for {@link #scopeResolver}.
* @see ClassPathBeanDefinitionScanner#setScopedProxyMode(ScopedProxyMode)
*/
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

/**
* Controls the class files eligible for component detection.
* <p>Consider use of {@link #includeFilters} and {@link #excludeFilters}
* for a more flexible approach.
*/
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;

/**
* Indicates whether automatic detection of classes annotated with {@code @Component}
* {@code @Repository}, {@code @Service}, or {@code @Controller} should be enabled.
*/
boolean useDefaultFilters() default true;

/**
* Specifies which types are eligible for component scanning.
* <p>Further narrows the set of candidate components from everything in {@link #basePackages}
* to everything in the base packages that matches the given filter or filters.
* <p>Note that these filters will be applied in addition to the default filters, if specified.
* Any type under the specified base packages which matches a given filter will be included,
* even if it does not match the default filters (i.e. is not annotated with {@code @Component}).
* @see #resourcePattern()
* @see #useDefaultFilters()
*/
Filter[] includeFilters() default {};

/**
* Specifies which types are not eligible for component scanning.
* @see #resourcePattern
*/
Filter[] excludeFilters() default {};

/**
* Specify whether scanned beans should be registered for lazy initialization.
* <p>Default is {@code false}; switch this to {@code true} when desired.
* @since 4.1
*/
boolean lazyInit() default false;


/**
* Declares the type filter to be used as an {@linkplain ComponentScan#includeFilters
* include filter} or {@linkplain ComponentScan#excludeFilters exclude filter}.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {

/**
* The type of filter to use.
* <p>Default is {@link FilterType#ANNOTATION}.
* @see #classes
* @see #pattern
*/
FilterType type() default FilterType.ANNOTATION;

/**
* Alias for {@link #classes}.
* @see #classes
*/
@AliasFor("classes")
Class<?>[] value() default {};

/**
* The class or classes to use as the filter.
* <p>The following table explains how the classes will be interpreted
* based on the configured value of the {@link #type} attribute.
* <table border="1">
* <tr><th>{@code FilterType}</th><th>Class Interpreted As</th></tr>
* <tr><td>{@link FilterType#ANNOTATION ANNOTATION}</td>
* <td>the annotation itself</td></tr>
* <tr><td>{@link FilterType#ASSIGNABLE_TYPE ASSIGNABLE_TYPE}</td>
* <td>the type that detected components should be assignable to</td></tr>
* <tr><td>{@link FilterType#CUSTOM CUSTOM}</td>
* <td>an implementation of {@link TypeFilter}</td></tr>
* </table>
* <p>When multiple classes are specified, <em>OR</em> logic is applied
* &mdash; for example, "include types annotated with {@code @Foo} OR {@code @Bar}".
* <p>Custom {@link TypeFilter TypeFilters} may optionally implement any of the
* following {@link org.springframework.beans.factory.Aware Aware} interfaces, and
* their respective methods will be called prior to {@link TypeFilter#match match}:
* <ul>
* <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
* <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
* <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
* <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
* </ul>
* <p>Specifying zero classes is permitted but will have no effect on component
* scanning.
* @since 4.2
* @see #value
* @see #type
*/
@AliasFor("value")
Class<?>[] classes() default {};

/**
* The pattern (or patterns) to use for the filter, as an alternative
* to specifying a Class {@link #value}.
* <p>If {@link #type} is set to {@link FilterType#ASPECTJ ASPECTJ},
* this is an AspectJ type pattern expression. If {@link #type} is
* set to {@link FilterType#REGEX REGEX}, this is a regex pattern
* for the fully-qualified class names to match.
* @see #type
* @see #classes
*/
String[] pattern() default {};

}

}

源码中属性含义解析

@ComponentScan注解中每个属性的含义

  • value: 作用同basePackages属性, String[] 数组类型,指定要扫描的包名.如果指定了要扫描的包名,则Spring会扫描指定的包及其子包下的所有类.

  • basePackages: 作用同value属性, String[] 数组类型,指定要扫描的包名.如果指定了要扫描的包名,则Spring会扫描指定的包及其子包下的所有类.

  • basePcakageClasses: Class<?>数组类型,指定要扫描的类的Class对象.该类所在包及其子包下的其他类也会被扫描并注入IOC容器. (例如: @ComponentScan(basePackageClasses = UserService.class),则 UserService所在包及其子包下的其他类也会被扫描)

  • nameGenerator: Class<? extends BeanNameGenerator>类型,指定扫描类时,向IOC注入Bean对象时的命名规则

  • scopeResolver: Class<? extends ScopeMetadataResolver类型,扫描类时,用于处理并转换符合条件的Bean的作用范围

  • scopeProxy: ScopeProxyMode类型,指定生成Bean对象时的处理方式,默认的处理方法是DEFAULT,也就是不使用代理.详细解释

  • resourcePattoern: String类型,用于指定扫描的文件类型,默认是扫描指定包下的**/.class(**表示当前包及其子包; /.class表示任意类名的字节码)

  • useDefaultFilters: boolean类型,是否自动检测@Component,@Repository, @Service, @Controller注解, 默认是true

  • includeFilters: Filter[]数组类型,自定义组件扫描过滤规则,符合过滤规则的类的Bean定义信息会被注册到IOC容器中.includeFilters表示只包含对应的规则,当使用includeFilters()来指定只包含哪些注解标注的类时,需要禁用默认的过滤规则,也就是需要将useDefaultFilters属性设置为false.并且,除了符合过滤规则的类外,Spring内置的一些类的Bean定义信息注册到IOC容器时不受过滤规则限制;如:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor

org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory

  • excludeFilters: Filter数组类型,自定义组件扫描过滤规则,excludeFilters表示排除使用对应的规则,符合过滤规则类的Bean定义信息不会注册到IOC容器中

  • lazyInit: boolean类型,从Spring 4.1版本开始提供,表示Spring扫描组件时采用懒加载, 默认false,表示不开启懒加载

@Filter注解中每个属性的含义

  • type: FilterType类型,表示过滤规则的类型.详细解释
  • value: Class<?>[]数组类型,过滤符合规则的类,作用同classes属性
  • classes: Class<?>[]数组类型,过滤符合规则的类,作用同value属性
  • pattern: 如果FilterType取值为ASPECTJ,则此属性表示ASPECTJ表达式

ScopedProxyMode

ScopedProxyMode枚举类表示Spring指定生成Bean对象时的代理方式

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
// ==================== 源码 ====================
/**
* @author Mark Fisher
* @since 2.5
* @see ScopeMetadata
*/
public enum ScopedProxyMode {

/**
* Default typically equals {@link #NO}, unless a different default
* has been configured at the component-scan instruction level.
*/
DEFAULT,

/**
* Do not create a scoped proxy.
* <p>This proxy-mode is not typically useful when used with a
* non-singleton scoped instance, which should favor the use of the
* {@link #INTERFACES} or {@link #TARGET_CLASS} proxy-modes instead if it
* is to be used as a dependency.
*/
NO,

/**
* Create a JDK dynamic proxy implementing <i>all</i> interfaces exposed by
* the class of the target object.
*/
INTERFACES,

/**
* Create a class-based proxy (uses CGLIB).
*/
TARGET_CLASS

}

ScopedProxyMode类是从Spring 2.5版本开始提供的枚举类; 属性含义

  • DEFAULT: 默认的代理方式,也就是不使用代理,除非在component-sacn级别使用了不同的配置
  • NO: 不使用代理
  • INTERFACES: 基于JDK动态代理实现接口代理对象
  • TARGET_CLASS: 基于CGLIB动态代理创建类代理对象

FilterType

FilterType枚举类表示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
// ==================== 源码 ====================
/**
* @author Mark Fisher
* @author Juergen Hoeller
* @author Chris Beams
* @since 2.5
* @see ComponentScan
* @see ComponentScan#includeFilters()
* @see ComponentScan#excludeFilters()
* @see org.springframework.core.type.filter.TypeFilter
*/
public enum FilterType {

/**
* Filter candidates marked with a given annotation.
* @see org.springframework.core.type.filter.AnnotationTypeFilter
*/
ANNOTATION,

/**
* Filter candidates assignable to a given type.
* @see org.springframework.core.type.filter.AssignableTypeFilter
*/
ASSIGNABLE_TYPE,

/**
* Filter candidates matching a given AspectJ type pattern expression.
* @see org.springframework.core.type.filter.AspectJTypeFilter
*/
ASPECTJ,

/**
* Filter candidates matching a given regex pattern.
* @see org.springframework.core.type.filter.RegexPatternTypeFilter
*/
REGEX,

/** Filter candidates using a given custom
* {@link org.springframework.core.type.filter.TypeFilter} implementation.
*/
CUSTOM

}

FilterType类是从Spring 2.5版本开始提供的枚举类,各个属性

  • ANNOTATION: 按照注解进行过滤
  • ASSIGNABLE_TYPE: 按照给定的类型进行过滤
  • ASPECTJ: 按照ASPECTJ表达式进行过滤
  • REGEX: 按照正则表达式进行过滤
  • CUSTOM: 按照自定义规则进行过滤,使用自定义过滤规则时,自定义的过滤器需要实现org.springframework.core.type.filter.TypeFilter接口

FilterType枚举类中,ANNOTATIONASSIGNABLE_TYPE是比较常用的,ASPECTJREGEX不太常用,如果FilterType枚举类无法满足日常开发需求时,可以通过实现org.springframework.core.type.filter.TypeFilter接口来自定义过滤规则,此时,将@Filtertype属性设置为FilterType.CUSTOM,classes属性设置为自定义规则的类对应的Class对象

使用案例

案例描述

使用自定义过滤规则实现Spring扫描指定包下的类时,使得名称中包含特定字符串的类符合过滤规则

案例实现

新增自定义过滤规则类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @author : igsshan
* @date : 2023/11/20 10:35
* @since : 1.0
*/
public class ComponentScanFilter implements TypeFilter {
/**
* 此方法返回一个 boolean 值, true 表示注入到 ioc 容器中
* @param metadataReader 表示读取到的当前正在扫描的类的信息
* @param metadataReaderFactory 表示可以获得到其他任何类的信息
* @return 返回值
* @throws IOException IOException
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
return className.contains("componentScanConfig");
}
}

新建配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @author : igsshan
* @date : 2023/11/20 10:39
* @since : 1.0
*/
@Configuration
@ComponentScan(value = "com.igsshan.springbootbasic.filter", includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = ComponentScanFilter.class)
}, useDefaultFilters = false)
public class ComponentScanConfig {

}

在自定义配置类ComponentScanConfig类上标注了@Configuration注解,表示ComponentScanConfig类是Spring的配置类;标注的@ComponentScan注解中指定了要扫描的包名,使用了只包含的过滤规则,并采用自定义过滤规则;

注意: 需要将 userDefaultFilters 设置成 false

测试结果

测试类

1
2
3
4
5
6
7
8
9
10
public class ComponentScanTest {
public static void main(String[] args) {

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ComponentScanConfig.class);
String[] beanDefinitionNames = context.getBeanDefinitionNames();
System.out.println("==========================");
Arrays.stream(beanDefinitionNames).forEach(System.out::println);
System.out.println("==========================");
}
}

测试结果

1
2
3
4
5
6
7
8
9
10
==========================
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
componentScanConfig
==========================

// 成功注入 componentScanConfig 类

其他案例

扫描时排除注解标注的类

排除@Controller,@Service@Repository注解,可以在配置类上通过@ComponentScan注解的excludeFilters()属性实现

1
2
3
4
5
6
7
8
9
10
11
/**
* @author : igsshan
* @date : 2023/11/20 11:58
* @since : 1.0
*/
@Configuration
@ComponentScan(value = {"com.igsshan.springbootbasic.core"}, excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class, Repository.class})
})
public class ExcludeAnnotationComponentScanConfig {
}

扫码时只包含注解标注的类

只包含哪些注解标注的类,可以使用@ComponentScan注解的excludeFilters()属性来指定Spring的包扫描;

注意: 使用excludeFilters()属性指定只包含哪些注解标注的类时, 需要禁用默认的过滤规则

1
2
3
4
5
6
7
8
9
10
11
/**
* @author : igsshan
* @date : 2023/11/20 14:18
* @since : 1.0
*/
@Configuration
@ComponentScan(value = {"com.igsshan.springbootbasic.core"}, includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)
public class IncludeAnnotationComponentScanConfig {
}

重复注解

Java 8找那个@ComponentScan注解是一个可重复注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @author : igsshan
* @date : 2023/11/20 14:25
* @since : 1.0
*/
@Configuration
@ComponentScan(value = {"com.igsshan.springbootbasic.core"}, includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)
@ComponentScan(value = {"com.igsshan.springbootbasic.core"}, includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Service.class})
}, useDefaultFilters = false)
public class JavaVersion8ComponentScanAnnotationConfig {
}

如果使用的是Java 8之前的版本,是无法使用多个@ComponentScan重复注解的.此时,可以在配置类上使用@ComponentScas注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author : igsshan
* @date : 2023/11/20 14:31
* @since : 1.0
*/
@Configuration
@ComponentScans(value = {
@ComponentScan(value = {"com.igsshan.springbootbasic.core"}, excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}),
@ComponentScan(value = {"com.igsshan.springbootbasic.core"}, includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Service.class})
})
})
public class JavaVersionNone8ComponentScanConfig {
}

扫描时按照指定的类型进行过滤

使用@ComponentScan注解进行包扫描时,按照给定的类型只包含TestService类 (接口)或其子类(实现类或者子接口)的组件

1
2
3
4
5
6
7
8
9
10
11
/**
* @author : igsshan
* @date : 2023/11/20 14:46
* @since : 1.0
*/
@Configuration
@ComponentScan(value = {"com.igsshan.springbootbasic.core"}, includeFilters = {
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {TestService.class})
}, useDefaultFilters = false)
public class CustomComponentScanConfig {
}

主要是TestService类型的组件,都会被加载到容器中,

TestService是一个Java类时,TestService类及其子类都会被加载都Spring容器中

TestService是一个接口时,TestService其子接口或实现类都会被加载到Spring容器中

扫描时按照ASPECTJ表达式进行过滤

使用@ComponentScan注解进行包扫描时,按照ASPECTJ表达式进行过滤

1
2
3
4
5
6
7
8
9
10
11
/**
* @author : igsshan
* @date : 2023/11/20 15:00
* @since : 1.0
*/
@Configuration
@ComponentScan(value = {"com.igsshan.springbootbasic.filter"}, includeFilters = {
@Filter(type = FilterType.ASPECTJ, classes = {AspectjFilter.class})
}, useDefaultFilters = false)
public class AspectjComponentScanConfig {
}

其中AspectjFilter类就是自定义的ASPECTJ表达式的过滤器类

扫描时按照正则表达式进行过滤

使用@ComponentScan注解进行包扫描时,按照正则表达式进行过滤

1
2
3
4
5
6
7
8
9
10
11
/**
* @author : igsshan
* @date : 2023/11/20 15:16
* @since : 1.0
*/
@Configuration
@ComponentScan(value = {"com.igsshan.springbootbasic.filter"}, includeFilters = {
@Filter(type = FilterType.ASPECTJ, classes = {RegexFilter.class})
}, useDefaultFilters = false)
public class RegexComponentScanConfig {
}

扫描时按照自定义规则进行过滤

如果要自定义规则过滤,自定义规则的类必须是org.springframework.core.type.filter.TypeFilter接口的实现类

1
2
3
4
5
6
7
8
9
10
11
/**
* @author : igsshan
* @date : 2023/11/20 15:52
* @since : 1.0
*/
public class CustomTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return false;
}
}

创建一个实现TypeFilter接口的类

当实现TypeFilter接口时,需要实现TypeFilter接口中的match()方法,match()方法返回值是boolean类型.

当返回true时,表示符合过滤规则,会将类的Bean定义信息注册到IOC容器中;

当返回false时,表示不符合过滤规则,对应的Bean定义信息不会注册到IOC容器中.

1
2
3
4
5
6
7
8
9
10
11
/**
* @author : igsshan
* @date : 2023/11/20 16:07
* @since : 1.0
*/
@Configuration
@ComponentScan(value = {"com.igsshan.springbootbasic.filter"}, excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = {CustomTypeFilter.class})
}, useDefaultFilters = false)
public class CustomTypeComponentScanConfig {
}