前言
@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 的源码如下:
<code>protectedSet
doScan(String
... basePackages) { Assert.notEmpty(basePackages,"At least one base package must be specified"
);Set
beanDefinitions =new
LinkedHashSet<>();for
(String
basePackage : basePackages) {Set
candidates = findCandidateComponents(basePackage);for
(BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata =this
.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName());String
beanName =this
.beanNameGenerator.generateBeanName(candidate,this
.registry);if
(candidateinstanceof
AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); }if
(candidateinstanceof
AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); }if
(checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder =new
BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder,this
.registry); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder,this
.registry); } } }return
beanDefinitions; }/<code>
上边的代码,从方法名,猜测:
findCandidateComponents:从classPath扫描组件,并转换为备选BeanDefinition,也就是要做的解析@Component的核心方法。
概要分析
findCandidateComponents在其父类
ClassPathScanningCandidateComponentProvider 中。
<code>public
class
ClassPathScanningCandidateComponentProvider
implements
EnvironmentCapable
,ResourceLoaderAware {
public
Set findCandidateComponents(String basePackage) {if
(this
.componentsIndex !=null
&& indexSupportsIncludeFilters()) {return
addCandidateComponentsFromIndex(this
.componentsIndex, basePackage); }else
{return
scanCandidateComponents(basePackage); } }private
Set scanCandidateComponents(String basePackage) { Set candidates = new LinkedHashSet<>();try
{ String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) +'/'
+this
.resourcePattern; Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);for
(Resource resource : resources) {if
(resource.isReadable()) {try
{ MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);if
(isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource);if
(isCandidateComponent(sbd)) { candidates.add(sbd); } }catch
(IOException ex) {return
candidates; } }/<code>
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其源码如下:
<code>protected
boolean
isCandidateComponent
(MetadataReader metadataReader)
throws
IOException {for
(TypeFilter tf :this
.includeFilters) {if
(tf.match(metadataReader, getMetadataReaderFactory())) {return
isConditionMatch(metadataReader); } }return
false
; }/<code>
includeFilters由registerDefaultFilters()设置初始值,有@Component,没有@Service啊?
<code>protected
void
registerDefaultFilters
()
{this
.includeFilters.add(new
AnnotationTypeFilter(Component.
class
)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.
class
.getClassLoader
();try
{this
.includeFilters.add(new
AnnotationTypeFilter( ((Class extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean"
, cl)),false
)); logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"
); }catch
(ClassNotFoundException ex) { }try
{this
.includeFilters.add(new
AnnotationTypeFilter( ((Class extends Annotation>) ClassUtils.forName("javax.inject.Named"
, cl)),false
)); logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"
); }catch
(ClassNotFoundException ex) { } }/<code>
Spring如何处理@Service的注解的呢????
2.查文档找思路
查阅官方文档,下面这话:
https://docs.spring.io/spring/docs/5.0.17.RELEASE/spring-framework-reference/core.html#beans-meta-annotations
@Component is a generic stereotype for any Spring-managed component. @Repository, @Service, and @Controller are specializations of @Component
大意如下:
@Component是任何Spring管理的组件的通用原型。@Repository、@Service和@Controller是派生自@Component。
<code>@Target
({ElementType.TYPE})@Retention
(RetentionPolicy.RUNTIME)@Documented
@Component
public@interface
Service {@AliasFor
(annotation = Component.class) String value() default""
; }/<code>
@Component是@Service的元注解,Spring 大概率,在读取@Service,也读取了它的元注解,并将@Service作为@Component处理。
3. 探寻@Component派生性流程
回顾
ClassPathScanningCandidateComponentProvider 中的关键的代码片段如下:
<code>private
Set scanCandidateComponents(String basePackage) { MetadataReader metadataReader =getMetadataReaderFactory().getMetadataReader(resource);if
(isCandidateComponent(metadataReader)){ } }public
final
MetadataReaderFactory getMetadataReaderFactory() {if
(this
.metadataReaderFactory ==null
) {this
.metadataReaderFactory = new CachingMetadataReaderFactory(); }return
this
.metadataReaderFactory; }/<code>
1. 确定metadataReader
CachingMetadataReaderFactory继承自
SimpleMetadataReaderFactory,就是对
SimpleMetadataReaderFactory加了一层缓存。
其内部的
SimpleMetadataReaderFactory#getMetadataReader 问:
<code>public
class
SimpleMetadataReaderFactory
implements
MetadataReaderFactory
{public
MetadataReadergetMetadataReader
(Resource resource)
throws
IOException {return
new
SimpleMetadataReader(resource,this
.resourceLoader.getClassLoader()); } }/<code>
这里可以看出
MetadataReader metadataReader =new SimpleMetadataReader(...);
2.查看match方法找重点方法
AnnotationTypeFilter#matchself方法如下:
<code>protected
boolean matchSelf(MetadataReader metadataReader) { AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();return
metadata.hasAnnotation(this
.annotationType.getName()) || (this
.considerMetaAnnotations && metadata.hasMetaAnnotation(this
.annotationType.getName())); }/<code>
是
metadata.hasMetaAnnotation法,从名称看是处理元注解,我们重点关注
逐步分析
找metadata.hasMetaAnnotation
metadata=metadataReader.getAnnotationMetadata();
metadataReader =new SimpleMetadataReader(...)
metadata= new SimpleMetadataReader#getAnnotationMetadata()
<code> SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException { InputStreamis
=new
BufferedInputStream(resource.getInputStream()); ClassReader classReader;try
{ classReader =new
ClassReader(is
); }catch
(IllegalArgumentException ex) {throw
new
NestedIOException("ASM ClassReader failed to parse class file - "
+"probably due to a new Java class file version that isn't supported yet: "
+ resource, ex); }finally
{is
.close(); } AnnotationMetadataReadingVisitor visitor =new
AnnotationMetadataReadingVisitor(classLoader); classReader.accept(visitor, ClassReader.SKIP_DEBUG);this
.annotationMetadata = visitor;this
.classMetadata = visitor;this
.resource = resource; }/<code>
metadata=new SimpleMetadataReader(...)**.**getAnnotationMetadata()= new
AnnotationMetadataReadingVisitor(。。)
也就是说
metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation
其方法如下:
<code>public
class
AnnotationMetadataReadingVisitor
{public
boolean hasMetaAnnotation(String metaAnnotationType) { Collection> allMetaTypes =this
.metaAnnotationMap.values();for
(Set metaTypes : allMetaTypes) {if
(metaTypes.contains(metaAnnotationType)) {return
true
; } }return
false
; } }/<code>
逻辑很简单,就是判断该注解的元注解在,在不在metaAnnotationMap中,如果在就返回true。
这里面核心就是metaAnnotationMap,搜索
AnnotationMetadataReadingVisitor类,没有发现赋值的地方??!。
查找metaAnnotationMap赋值
回到SimpleMetadataReader 的方法,
<code> SimpleMetadataReader(Resource resource, ClassLoader classLoader)throws
IOException { AnnotationMetadataReadingVisitor visitor =new
AnnotationMetadataReadingVisitor(classLoader); classReader.accept(visitor, ClassReader.SKIP_DEBUG);this
.annotationMetadata = visitor; } /<code>
发现一个可疑的语句:classReader.accept。
查看accept方法
<code>public
class
ClassReader
{public
void
accept
(..省略代码)
{ readElementValues( classVisitor.visitAnnotation(annotationDescriptor,true
), currentAnnotationOffset,true
, charBuffer); } }/<code>
查看readElementValues方法
<code>public
class
ClassReader
{private
int
readElementValues
(
final
AnnotationVisitor annotationVisitor,final
int
annotationOffset,final
boolean
named,final
char
[] charBuffer) {int
currentOffset = annotationOffset;int
numElementValuePairs = readUnsignedShort(currentOffset); currentOffset +=2
;if
(named) {while
(numElementValuePairs-- >0
) { String elementName = readUTF8(currentOffset, charBuffer); currentOffset = readElementValue(annotationVisitor, currentOffset +2
, elementName, charBuffer); } }else
{while
(numElementValuePairs-- >0
) { currentOffset = readElementValue(annotationVisitor, currentOffset,null
, charBuffer); } }if
(annotationVisitor !=null
) { annotationVisitor.visitEnd(); }return
currentOffset; } }/<code>
这里面的核心就是
annotationVisitor.visitEnd();
确定annotationVisitor
这里的annotationVisitor=
AnnotationMetadataReadingVisitor#visitAnnotation
源码如下,注意这里传递了metaAnnotationMap!!
<code>public
class
AnnotationMetadataReadingVisitor
{public
AnnotationVisitor visitAnnotation(String desc, boolean visible) { String className = Type.getType(desc).getClassName();this
.annotationSet.add(className);return
new AnnotationAttributesReadingVisitor( className,this
.attributesMap,this
.metaAnnotationMap,this
.classLoader); } }/<code>
annotationVisitor=AnnotationAttributesReadingVisitor
查阅annotationVisitor.visitEnd()
annotationVisitor=AnnotationAttributesReadingVisitor#visitEnd()
<code>public
class
AnnotationAttributesReadingVisitor
{public
void visitEnd() {super
.visitEnd(); Class extends Annotation> annotationClass =this
.attributes.annotationType();if
(annotationClass !=null
) { List attributeList =this
.attributesMap.get
(this
.annotationType);if
(attributeList ==null
) {this
.attributesMap.add(this
.annotationType,this
.attributes); }else
{ attributeList.add(0
,this
.attributes); }if
(!AnnotationUtils.isInJavaLangAnnotationPackage(annotationClass.getName())) {try
{ Annotation[] metaAnnotations = annotationClass.getAnnotations();if
(!ObjectUtils.isEmpty(metaAnnotations)) { Set visited = new LinkedHashSet<>();for
(Annotation metaAnnotation : metaAnnotations) { recursivelyCollectMetaAnnotations(visited, metaAnnotation); }if
(!visited.isEmpty()) { Set metaAnnotationTypeNames = new LinkedHashSet<>(visited.size());for
(Annotation ann : visited) { metaAnnotationTypeNames.add(ann.annotationType().getName()); }this
.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames); } } }catch
(Throwable ex) {if
(logger.isDebugEnabled()) { logger.debug("Failed to introspect meta-annotations on "
+ annotationClass +": "
+ ex); } } } } } }/<code>
内部方法
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