SpringBoot-注解@ComponentScan和注解@ComponentScans
参考文献
@ComponentScan
@ComponentScan
用于批量注册Bean.这个注解会让Spring去扫描某些包及其子包中所有的类.然后将满足一定条件的类作为Bean注册到Spring容器中.
1 | // org.springframework.context.annotation.ComponentScan |
工作流程
-
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
*/
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) {
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
Filter {
FilterType type() default FilterType.ANNOTATION;
Class<?>[] value() default {};
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
实现步骤
-
设置
@Filter
中type
的类型为:FilterType.CUSTOM
-
自定义过滤器类,需要实现接口:
org.springframework.core.type.filter.TypeFilter
1
2
3
4
5
6
7
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
22public class MyFilter implements TypeFilter {
/**
* @param metadataReader
* @param metadataReaderFactory
* @return
* @throws IOException
*/
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
11import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
public class ScanBean5 {
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 HoleLin's Blog!