点击上方“匠心零度”,选择“设为星标”

做积极的人,而不是积极废人

来源:my.oschina.net/floor/blog/4325651

  • 前言
  • 1.@Component解析流程
    • 找入口
    • 找核心方法
    • 概要分析
    • 2.查文档找思路
  • 3. 探寻@Component派生性流程
    • 1. 确定metadataReader
    • 2.查看match方法找重点方法
    • 逐步分析
  • 总结

前言

@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 的源码如下:

protected Set doScan(String... basePackages) {   Assert.notEmpty(basePackages, "At least one base package must be specified");   Set beanDefinitions = new LinkedHashSet<>();for (String basePackage : basePackages) {//findCandidateComponents 读资源装换为BeanDefinition      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 (candidate instanceof AbstractBeanDefinition) {            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);         }if (candidate instanceof 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;}

上边的代码,从方法名,猜测:

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

概要分析

findCandidateComponents在其父类ClassPathScanningCandidateComponentProvider 中。

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;}}

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其源码如下:

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {    //省略部分代码   for (TypeFilter tf : this.includeFilters) {      if (tf.match(metadataReader, getMetadataReaderFactory())) {         return isConditionMatch(metadataReader);      }   }   return false;}

includeFilters由registerDefaultFilters()设置初始值,有@Component,没有@Service啊?

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) {      // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.   }   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) {      // JSR-330 API not available - simply skip.   }}

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。

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented// @Service 派生自@Component@Componentpublic @interface Service {

   /**    * The value may indicate a suggestion for a logical component name,    * to be turned into a Spring bean in case of an autodetected component.    * @return the suggested component name, if any (or empty String otherwise)    */   @AliasFor(annotation = Component.class)   String value() default "";

}

@Component是@Service的元注解,Spring 大概率,在读取@Service,也读取了它的元注解,并将@Service作为@Component处理。

3. 探寻@Component派生性流程

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

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;}

1. 确定metadataReader

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

其内部的SimpleMetadataReaderFactory#getMetadataReader 为:

public class SimpleMetadataReaderFactory implements MetadataReaderFactory {    @Overridepublic MetadataReader getMetadataReader(Resource resource) throws IOException {   return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());}    }

这里可以看出

MetadataReader metadataReader =new SimpleMetadataReader(...);

2.查看match方法找重点方法


AnnotationTypeFilter#matchself方法如下:

@Overrideprotected boolean matchSelf(MetadataReader metadataReader) {   AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();   return metadata.hasAnnotation(this.annotationType.getName()) ||         (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));}

是metadata.hasMetaAnnotation法,从名称看是处理元注解,我们重点关注

逐步分析

找metadata.hasMetaAnnotation

metadata=metadataReader.getAnnotationMetadata();

metadataReader =new SimpleMetadataReader(...)

metadata= new SimpleMetadataReader#getAnnotationMetadata()

//SimpleMetadataReader 的构造方法SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {   InputStream is = 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;   // (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)   this.classMetadata = visitor;   this.resource = resource;}

metadata=new SimpleMetadataReader(...)**.**getAnnotationMetadata()= new AnnotationMetadataReadingVisitor(。。)

也就是说

metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation

其方法如下:

public class AnnotationMetadataReadingVisitor{    // 省略部分代码@Overridepublic boolean hasMetaAnnotation(String metaAnnotationType) {   Collection> allMetaTypes = this.metaAnnotationMap.values();for (Set metaTypes : allMetaTypes) {if (metaTypes.contains(metaAnnotationType)) {return true;      }   }return false;}}

逻辑很简单,就是判断该注解的元注解在,在不在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{@Overridepublic 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{@Overridepublic 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);            }         }      }   }}}

内部方法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

END

如果读完觉得有收获的话,欢迎点【好看】,关注【匠心零度】,查阅更多精彩历史!!!

让我“好看” 

@bean注解和@component注解的区别_通过源码查看 @Component,@Service 等注解是如何被解析的...相关推荐

  1. datatable如何生成级联数据_通过源码分析Mybatis是如何返回数据库生成的自增主键值?...

    在Mybatis中,执行insert操作时,如果我们希望返回数据库生成的自增主键值,那么就需要使用到KeyGenerator对象. 需要注意的是,KeyGenerator的作用,是返回数据库生成的自增 ...

  2. 通过源码理解反射与注解是什么东西?

    反射+注解(Java):练习(Java) 1.通过反射获得注解 2.获得注解value的值 3.获得注解value的值 代码示例: package reflect;import java.lang.a ...

  3. java中j 和 j啥区别_从字节码层次分析++j和j++的区别

    一.缘起 最近看到个面试题: int j = 0; for(int i = 0; i <100; i++) j = j++; System.out.println(j); 输出结果是0,如果换成 ...

  4. it工程师和码农的区别_工程师和码农的最大区别在哪

    工程师和码农的区别并不在于技术水准的高低. 工程师注重解决问题. 工程师勇于承担责任. 工程师善于控制进度. 工程师能够自由沟通. 工程师知道如何合理安排时间. 工程师明白实现的功能有什么意义. 工程 ...

  5. it工程师和码农的区别_你是码农,还是IT工程师?

    IT界知名的段子手曾说:对于那些月薪两万以下,自称IT工程师的码农们,其实我们从来没有把他们归为我们IT工程师的队伍.他们虽然总是以IT工程师自居,但只是他们一厢情愿罢了. 此话一出,不知激起了多少小 ...

  6. @Autowired注解通过源码讲原理

    https://blog.csdn.net/o9109003234/article/details/101365065

  7. @Component与@Configuration区别

    @Component与@Configuration区别: @Configuration本质上还是@Component. @Configuration标记的类必须符合下面的要求: 1.配置类不能是 fi ...

  8. 高并发编程-深入分析wait和sleep的区别并结合源码示例佐证

    文章目录 概述 区别 sleep是Thread的方法,wait是Object的方法 sleep不会释放锁(Monitor), wait会让当前线程释放锁 sleep 不依赖 Monitor,但是wai ...

  9. java spring框架 注解_史上最全的java spring注解

    史上最全的java spring注解,没有之一 注解是个好东西,但好东西我们也是看见过,整理过,理解过,用过才知道好.不求我们每个都记住,但求保有印象,在需要的时候能提取出来再查找相关资料,平时工作就 ...

最新文章

  1. 5折交叉验证_[Machine Learning] 模型评估——交叉验证/K折交叉验证
  2. lstm 文本纠错_工业界纠错系统
  3. session实现购物车
  4. Python装饰器详解,详细介绍它的应用场景
  5. sql server 面试_SQL Server审核面试问题
  6. Memcached主主复制+Keepalived高可用架构
  7. Linux 下 ascii 的查看方式
  8. 状态管理模式 — Vuex如何使用?
  9. jquery调用WebService和WebService输出JSON
  10. OpenSSL 常用函数——证书操作
  11. 家用计算机常见故障及解决方式,计算机常见故障及解决方法
  12. 以图搜图(基于 Milvus 和 VGG 实现以图搜图)
  13. It's only too late if you decide it is. Get busy living, or get busy dying(转)
  14. 支持10K高清视频是HDMI 2.1有什么特点?
  15. 华为S5700交换机链路聚合配置
  16. 明日书苑|硬笔书法临写指南
  17. 使用visjs制作网络拓扑图
  18. QQ看点模块(初稿截图)
  19. 海信网络科技软件研发实习面试
  20. R——R的一些边边角角

热门文章

  1. 如何优化大规模推荐?下一代算法技术JTM来了
  2. 优化 Tengine HTTPS 握手时间
  3. X-Pack Spark归档POLARDB数据做分析
  4. 像数据科学家一样思考:12步指南(下)
  5. 阿里云DDoS高防 - 访问与攻击日志实时分析(三)
  6. 看阿里云如何为直播用户营造临场沉浸感?
  7. “刺激的”2017双11 阿里安全工程师首度揭秘智能风控平台MTEE3
  8. 你知道吗?其实 Oracle 直方图自动统计算法存在这些缺陷!(附验证步骤)
  9. 我是如何用6个月,从0编程经验变成数据科学家的?
  10. 2025年将达6.4万亿美元!AI这下玩大了……