Spring中@Component,@Service等注解如何被解析?
https://mp.weixin.qq.com/s/QaIftFx7B9qa48a0GVI5-g
作者 | 温安适
来源 | http://8rr.co/EjqL
这个系列分为5篇,这是首篇
1.@Component,@Service等注解是如何被解析的
2.@Enableq驱动原理
3.@EnableAutoConfiguration处理逻辑
4.spring,springBoot事件(最晚20200719)
5.自定义springboot starter(最晚20200726)
前言
@Component和@Service都是工作中常用的注解,Spring如何解析?
1.@Component解析流程
找入口
Spring Framework2.0开始,引入可扩展的XML编程机制,该机制要求XML Schema命名空间需要与Handler建立映射关系。
该关系配置在相对于classpath下的/META-INF/spring.handlers中。

如上图所示 ContextNamespaceHandler对应context:... 分析的入口。
找核心方法
浏览ContextNamespaceHandler

在parse中有一个很重要的注释
// Actually scan for bean definitions and register them.
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
大意是:ClassPathBeanDefinitionScanner#doScan是扫描BeanDefinition并注册的实现 。
ClassPathBeanDefinitionScanner 的源码如下:
上边的代码,从方法名,猜测:
findCandidateComponents:从classPath扫描组件,并转换为备选BeanDefinition,也就是要做的解析@Component的核心方法。
概要分析
findCandidateComponents在其父类ClassPathScanningCandidateComponentProvider 中。
findCandidateComponents大体思路如下:
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX resolveBasePackage(basePackage) + '/' + this.resourcePattern; 将package转化为ClassLoader类资源搜索路径packageSearchPath,例如:com.wl.spring.boot转化为classpath:com/wl/spring/boot/**/.class
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); 加载搜素路径下的资源。
isCandidateComponent 判断是否是备选组件
candidates.add(sbd); 添加到返回结果的list
ClassPathScanningCandidateComponentProvider#isCandidateComponent其源码如下:
includeFilters由registerDefaultFilters()设置初始值,有@Component,没有@Service啊?
Spring如何处理@Service的注解的呢????
2.查文档找思路
查阅官方文档,下面这话:
@Component is a generic stereotype for any Spring-managed component. @Repository, @Service, and @Controller are specializations of @Component
大意如下:
@Component是任何Spring管理的组件的通用原型。@Repository、@Service和@Controller是派生自@Component。
@Component是@Service的元注解,Spring 大概率,在读取@Service,也读取了它的元注解,并将@Service作为@Component处理。
3. 探寻@Component派生性流程
回顾ClassPathScanningCandidateComponentProvider 中的关键的代码片段如下:
1. 确定metadataReader
CachingMetadataReaderFactory继承自 SimpleMetadataReaderFactory,就是对SimpleMetadataReaderFactory加了一层缓存。
其内部的SimpleMetadataReaderFactory#getMetadataReader 为:
这里可以看出
MetadataReader metadataReader =new SimpleMetadataReader(...);
2.查看match方法找重点方法

AnnotationTypeFilter#matchself方法如下:
是metadata.hasMetaAnnotation法,从名称看是处理元注解,我们重点关注
逐步分析
找metadata.hasMetaAnnotation
metadata=metadataReader.getAnnotationMetadata();
metadataReader =new SimpleMetadataReader(...)
metadata= new SimpleMetadataReader#getAnnotationMetadata()
metadata=new SimpleMetadataReader(...).getAnnotationMetadata()= new AnnotationMetadataReadingVisitor(。。)
也就是说
metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation
其方法如下:
逻辑很简单,就是判断该注解的元注解在,在不在metaAnnotationMap中,如果在就返回true。
这里面核心就是metaAnnotationMap,搜索AnnotationMetadataReadingVisitor类,没有发现赋值的地方??!。
查找metaAnnotationMap赋值
回到SimpleMetadataReader 的方法,
发现一个可疑的语句:classReader.accept。
查看accept方法
查看readElementValues方法
这里面的核心就是 annotationVisitor.visitEnd();
确定annotationVisitor
这里的annotationVisitor=AnnotationMetadataReadingVisitor#visitAnnotation
源码如下,注意这里传递了metaAnnotationMap!!
annotationVisitor=AnnotationAttributesReadingVisitor
查阅annotationVisitor.visitEnd()
annotationVisitor=AnnotationAttributesReadingVisitor#visitEnd()
内部方法recursivelyCollectMetaAnnotations 递归的读取注解,与注解的元注解(读@Service,再读元注解@Component),并设置到metaAnnotationMap,也就是AnnotationMetadataReadingVisitor 中的metaAnnotationMap中。
总结
大致如下:
ClassPathScanningCandidateComponentProvider#findCandidateComponents
将package转化为ClassLoader类资源搜索路径packageSearchPath
加载搜素路径下的资源。
isCandidateComponent 判断是否是备选组件。
内部调用的TypeFilter的match方法:
AnnotationTypeFilter#matchself中metadata.hasMetaAnnotation处理元注解
metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation
就是判断当前注解的元注解在不在metaAnnotationMap中。
AnnotationAttributesReadingVisitor#visitEnd()内部方法recursivelyCollectMetaAnnotations 递归的读取注解,与注解的元注解(读@Service,再读元注解@Component),并设置到metaAnnotationMap
添加到返回结果的list
Last updated
Was this helpful?