@Component 注解原理

缘起

@Component注解大家都用过,就是用于注册组件bean的,但是它的原理是什么呢? 且听我慢慢道来. debug的例子见【1】

分析

一般@Component注解(包括它的子注解,所谓子注解指的是注解上面标注了 @Component的注解)的用法都是结合配置类上扫包@ComponentScan.

调用栈如下

1
2
3
4
5
6
7
8
9
10
11
12
13
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
...
refresh();
}
-->
org.springframework.context.support.AbstractApplicationContext.refresh()中的
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
-->
org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(BeanDefinitionRegistry) {
...

}

其实至此,和【1】的源码2是一样的(都是ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry接口被调用),不一样的地方在此就开始了. 首先上面第十行的源码是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
...
do {
parser.parse(candidates);
parser.validate();

Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);

// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
...

​ 源码1

【1】中的源码2走的是源码1的第15行

1
this.reader.loadBeanDefinitions(configClasses);

但是这里走的是源码1的第3行.

1
parser.parse(candidates);

这里跟几步就到了

org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClass, SourceClass)

1
2
3
4
5
6
7
8
9
10
11
12
13
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
...
// 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());
...

​ 源码2

源码2的第12行跟进去将来到以下源码,传入componentScan参数.

org.springframework.context.annotation.ComponentScanAnnotationParser.parse(AnnotationAttributes, String)

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
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
...
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
...
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}

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

Set<String> basePackages = new LinkedHashSet<String>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
basePackages.addAll(Arrays.asList(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));
}

​ 源码3

这里的scanner很有意思,它加了很多includeFilter和excludeFilter(源码3的第8行和第13行), 这些东西都是从@ComponentScan注解的属性解读出来的. 何以为证? 源码3的scanner操作的依据都是 componentScan这个属性,而这个属性是源码2提供的. 而componentScan这个属性在源码2中就是根据 @ComponentScans 和 @ComponentScan 注解的解读来的(源码2的第五行).

所以我们就知道了——如果想自定义包扫描规则的话,可以从@ComponentScan 注解 的includeFilters属性入手. 这个我写过例子的(见【2】). 那么 @Component用在哪里呢? 我们现在还是不知道啊~ 源码3的第三行初始化scanner的地方就用到了 @Component 注解.

跟进去, 就是ClassPathBeanDefinitionScanner的构造器

1
2
3
4
5
6
7
8
9
10
11
12
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, ResourceLoader resourceLoader) {

Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;

if (useDefaultFilters) {
registerDefaultFilters();
}
setEnvironment(environment);
setResourceLoader(resourceLoader);
}

​ 源码4

跟进源码4的第八行,

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.registerDefaultFilters()

1
2
3
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
...

​ 源码5

看到了没有? 源码3的scanner初始化就有了 @Component 注解——即只有标有这个注解的扫包范围内的bean才会被纳入考虑. 什么意思? 我们继续跟进源码3的第42行,

org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(String…)

1
2
3
4
5
6
7
8
9
10
11
12
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
...
for (BeanDefinition candidate : candidates) {
...
registerBeanDefinition(definitionHolder, this.registry);
}
return beanDefinitions;
}

​ 源码6

其中第五行就是找符合条件的bean. 继续跟进去

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(String)

1
2
3
...
if (isCandidateComponent(metadataReader)) {
...

​ 源码7

继续跟

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.isCandidateComponent(MetadataReader)

1
2
3
4
5
6
7
...
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return isConditionMatch(metadataReader);
}
}
...

​ 源码8

这里的includeFilters就是源码3在第8行加入的以及源码3第三行初始化的时候加入的,亦即源码5第二行加入的new AnnotationTypeFilter(Component.class), 而AnnotationTypeFilter的match方法并不是它自己的,而是它的父类AbstractTypeHierarchyTraversingFilter的match方法.

即org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter.match(MetadataReader, MetadataReaderFactory)

1
2
3
4
if (matchSelf(metadataReader)) {
return true;
}
...

​ 源码9

而matchSelf方法放在子类AnnotationTypeFilter中实现(典型的模板设计模式)

org.springframework.core.type.filter.AnnotationTypeFilter.matchSelf(MetadataReader)

1
2
3
4
5
6
@Override
protected boolean matchSelf(MetadataReader metadataReader) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
return metadata.hasAnnotation(this.annotationType.getName()) ||
(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}

​ 源码10

这里源码10的annotationType就是new AnnotationTypeFilter(Component.class)传入的Component.class

所以这里就是判断有无 @Component注解. 判断为true之后就一路回到源码6的第五行——拿到了符合要求的

candidates, 然后来到源码6的第九行,跟几步就来到了

org.springframework.beans.factory.support.BeanDefinitionReaderUtils.registerBeanDefinition(BeanDefinitionHolder, BeanDefinitionRegistry)

1
2
3
...
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
...

​ 源码11

而这里的registry就是DefaultListableBeanFactory. 源码11的这里就开始注册bean定义了. 而注册bean定义的过程在【3】等处多次讲过了.

至此,@Component的原理已经理清楚了…

参考

【1】https://yfsyfs.github.io/2019/06/23/Configuration-%E6%B3%A8%E8%A7%A3%E6%BA%90%E7%A0%81/

DEMO

【1】https://github.com/yfsyfs/backend/tree/master/spring-annotation-applicationlistener

【2】https://github.com/yfsyfs/backend/tree/master/spring-annotation-componentscan5

【3】https://yfsyfs.github.io/2019/06/13/Profile-%E5%8E%9F%E7%90%86/