@Component,@Service等注解是如何被解析的?

您所在的位置:网站首页 @service注解 @Component,@Service等注解是如何被解析的?

@Component,@Service等注解是如何被解析的?

2022-10-18 17:24| 来源: 网络整理| 查看: 265

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录 前言1、@Component解析流程找入口找核心方法概要分析 2、查文档找思路3.探寻@Component派生性流程确定metadataReader查看match方法找重点方法逐步分析找metadata.hasMetaAnnotation查找metaAnnotationMap赋值确定annotationVisitor查阅annotationVisitor.visitEnd() 总结

前言

@Component和@Service都是工作中常用的注解,Spring如何解析?

提示:以下是本篇文章正文内容,下面案例可供参考

1、@Component解析流程 找入口

Spring Framework2.0开始,引入可扩展的XML编程机制,该机制要求XML Schema命名空间需要与Handler建立映射关系。

该关系配置在相对于classpath下的/META-INF/spring.handlers中。 在这里插入图片描述 如上图所示 ContextNamespaceHandler对应context:… 分析的入口。

找核心方法

浏览ContextNamespaceHandler 在这里插入图片描述 点进ComponentScanBeanDefinitionParser方法进入类中,在parse中有一个很重要的注释 在这里插入图片描述 大意是:ClassPathBeanDefinitionScanner#doScan是扫描BeanDefinition并注册的实现 。

ClassPathBeanDefinitionScanner 的源码如下: 在这里插入图片描述 上边的代码,从方法名,猜测:

findCandidateComponents:从classPath扫描组件,并转换为备选BeanDefinition,也就是要做的解析@Component的核心方法。

概要分析

findCandidateComponents在其父类ClassPathScanningCandidateComponentProvider 中。 在这里插入图片描述 详细代码可以自行查看。 findCandidateComponents大体思路如下:

1.String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX resolveBasePackage(basePackage) + ‘/’ + this.resourcePattern; 将package转化为ClassLoader类资源 搜索路径packageSearchPath,例如:com.wl.spring.boot转化为classpath*:com/wl/spring/boot/**/*.class 2.Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); 加载搜素 路径下的资源。 3.isCandidateComponent 判断是否是备选组件 4.candidates.add(sbd); 添加到返回结果的list

ClassPathScanningCandidateComponentProvider#isCandidateComponent其源码如下: 在这里插入图片描述 includeFilters由registerDefaultFilters()设置初始值,有@Component,没有@Service啊? 在这里插入图片描述 Spring如何处理@Service的注解的呢????

2、查文档找思路

查阅官方文档,下面这话:

https://docs.spring.io/spring/docs/5.0.17.RELEASE/spring-framework-reference/core.html#beans-meta-annotations

在这里插入图片描述 大意如下:

@Component是任何Spring管理的组件的通用原型。@Repository、@Service和@Controller是派生自@Component。

在这里插入图片描述 @Component是@Service的元注解,Spring 大概率,在读取@Service,也读取了它的元注解,并将@Service作为@Component处理。

3.探寻@Component派生性流程

回顾ClassPathScanningCandidateComponentProvider 中的关键的代码片段如下:

在这里插入图片描述

确定metadataReader

CachingMetadataReaderFactory继承自 SimpleMetadataReaderFactory,就是对SimpleMetadataReaderFactory加了一层缓存。

其内部的SimpleMetadataReaderFactory#getMetadataReader 为: 在这里插入图片描述 这里可以看出

MetadataReader metadataReader =new SimpleMetadataReader(…);

查看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 的方法,

//这个accept方法,很可疑,在赋值之前执行 SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException { //省略其他代码 AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader); classReader.accept(visitor, ClassReader.SKIP_DEBUG); this.annotationMetadata = visitor; }

发现一个可疑的语句:classReader.accept。

查看accept方法

public class ClassReader { //省略其他代码 public void accept(..省略代码){ //省略其他代码 readElementValues( classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true), currentAnnotationOffset, true, charBuffer); } }

查看readElementValues方法

public class ClassReader{ //省略其他代码 private int readElementValues( final AnnotationVisitor annotationVisitor, final int annotationOffset, final boolean named, final char[] charBuffer) { int currentOffset = annotationOffset; // Read the num_element_value_pairs field (or num_values field for an array_value). int numElementValuePairs = readUnsignedShort(currentOffset); currentOffset += 2; if (named) { // Parse the element_value_pairs array. while (numElementValuePairs-- > 0) { String elementName = readUTF8(currentOffset, charBuffer); currentOffset = readElementValue(annotationVisitor, currentOffset + 2, elementName, charBuffer); } } else { // Parse the array_value array. while (numElementValuePairs-- > 0) { currentOffset = readElementValue(annotationVisitor, currentOffset, /* named = */ null, charBuffer); } } if (annotationVisitor != null) { annotationVisitor.visitEnd(); } return currentOffset; } }

这里面的核心就是 annotationVisitor.visitEnd();

确定annotationVisitor

这里的annotationVisitor=AnnotationMetadataReadingVisitor#visitAnnotation

源码如下,注意这里传递了metaAnnotationMap!!

public class AnnotationMetadataReadingVisitor{ @Override 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); } }

annotationVisitor=AnnotationAttributesReadingVisitor

查阅annotationVisitor.visitEnd()

annotationVisitor=AnnotationAttributesReadingVisitor#visitEnd()

public class AnnotationAttributesReadingVisitor{ @Override public void visitEnd() { super.visitEnd(); Class 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); } } } } } }

内部方法recursivelyCollectMetaAnnotations 递归的读取注解,与注解的元注解(读@Service,再读元注解@Component),并设置到metaAnnotationMap,也就是AnnotationMetadataReadingVisitor 中的metaAnnotationMap中。

总结

大致如下:

ClassPathScanningCandidateComponentProvider#findCandidateComponents 1.将package转化为ClassLoader类资源搜索路径packageSearchPath 2.加载搜素路径下的资源。 3.isCandidateComponent 判断是否是备选组件。 内部调用的TypeFilter的match方法:

AnnotationTypeFilter#matchself中metadata.hasMetaAnnotation处理元注解 metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation 就是判断当前注解的元注解在不在metaAnnotationMap中。 AnnotationAttributesReadingVisitor#visitEnd()内部方法recursivelyCollectMetaAnnotations 递归的读 取注解,与注解的元注解(读@Service,再读元注解@Component),并设置到metaAnnotationMap

4.添加到返回结果的list



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3