热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

@bean注解和@component注解的区别_@Component,@Service等注解是如何被解析的

以下文章来源于http:8rr.coEjqL,作者温安适前言Component和Service都是工作中常用的注解,Spring如何解析?

以下文章来源于http://8rr.co/EjqL,作者温安适

前言

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

1.@Component解析流程

找入口

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

该关系配置在相对于classpath下的/META-INF/spring.handlers中。

7fb9dfde0bfbf6446804cab8727cd655.png

如上图所示 ContextNamespaceHandler对应<...> 分析的入口。

找核心方法

浏览ContextNamespaceHandler

60e002de6a0d2b8957b1c592e43c8008.gif

在parse中有一个很重要的注释

// Actually scan for bean definitions and register them.

ClassPathBeanDefinitionScanner scanner  &#61; configureScanner(parserContext, element);

大意是&#xff1a;ClassPathBeanDefinitionScanner#doScan是扫描BeanDefinition并注册的实现。

ClassPathBeanDefinitionScanner 的源码如下&#xff1a;

protected Set doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set beanDefinitions &#61; new LinkedHashSet<>();for (String basePackage : basePackages) {//findCandidateComponents 读资源转换为BeanDefinition
      Set candidates &#61; findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {
         ScopeMetadata scopeMetadata &#61; this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         candidate.setScope(scopeMetadata.getScopeName());
         String beanName &#61; 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 &#61; new BeanDefinitionHolder(candidate, beanName);
            definitionHolder &#61;
                  AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);
            registerBeanDefinition(definitionHolder, this.registry);
         }
      }
   }return beanDefinitions;
}

上边的代码&#xff0c;从方法名&#xff0c;猜测&#xff1a;

findCandidateComponents&#xff1a;从classPath扫描组件&#xff0c;并转换为备选BeanDefinition&#xff0c;也就是要做的解析&#64;Component的核心方法。

概要分析

findCandidateComponents在其父类ClassPathScanningCandidateComponentProvider 中。

public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
//省略其他代码
public Set findCandidateComponents(String basePackage) {
   if (this.componentsIndex !&#61; null && indexSupportsIncludeFilters()) {
      return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
   }
   else {
      return scanCandidateComponents(basePackage);
   }
}
private Set scanCandidateComponents(String basePackage) {
   Set candidates &#61; new LinkedHashSet<>();try {
      String packageSearchPath &#61; ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX &#43;
            resolveBasePackage(basePackage) &#43; &#39;/&#39; &#43; this.resourcePattern;
      Resource[] resources &#61; getResourcePatternResolver().getResources(packageSearchPath);//省略部分代码for (Resource resource : resources) {//省略部分代码if (resource.isReadable()) {try {
               MetadataReader metadataReader &#61; getMetadataReaderFactory().getMetadataReader(resource);if (isCandidateComponent(metadataReader)) {
                  ScannedGenericBeanDefinition sbd &#61; new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setSource(resource);if (isCandidateComponent(sbd)) {
                     candidates.add(sbd);//省略部分代码
      }
   }catch (IOException ex) {//省略部分代码 }return candidates;
}
}

findCandidateComponents大体思路如下&#xff1a;

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

ClassPathScanningCandidateComponentProvider#isCandidateComponent其源码如下&#xff1a;

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

includeFilters由registerDefaultFilters()设置初始值&#xff0c;有&#64;Component&#xff0c;没有&#64;Service啊&#xff1f;

protected void registerDefaultFilters() {
   this.includeFilters.add(new AnnotationTypeFilter(Component.class));
   ClassLoader cl &#61; ClassPathScanningCandidateComponentProvider.class.getClassLoader();
   try {
      this.includeFilters.add(new AnnotationTypeFilter(
            ((Class extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
      logger.trace("JSR-250 &#39;javax.annotation.ManagedBean&#39; 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 &#39;javax.inject.Named&#39; annotation found and supported for component scanning");
   }
   catch (ClassNotFoundException ex) {
      // JSR-330 API not available - simply skip.
   }
}

Spring如何处理&#64;Service的注解的呢&#xff1f;&#xff1f;&#xff1f;&#xff1f;

2.查文档找思路

查阅官方文档&#xff0c;下面这话&#xff1a;

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

&#64;Component is a generic stereotype for any Spring-managed component. &#64;Repository, &#64;Service, and &#64;Controller are specializations of &#64;Component

大意如下&#xff1a;

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

&#64;Target({ElementType.TYPE})
&#64;Retention(RetentionPolicy.RUNTIME)
&#64;Documented
// &#64;Service 派生自&#64;Component
&#64;Component
public &#64;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.
    * &#64;return the suggested component name, if any (or empty String otherwise)
    */
   &#64;AliasFor(annotation &#61; Component.class)
   String value() default "";

}

&#64;Component是&#64;Service的元注解&#xff0c;Spring 大概率&#xff0c;在读取&#64;Service&#xff0c;也读取了它的元注解&#xff0c;并将&#64;Service作为&#64;Component处理。

3. 探寻&#64;Component派生性流程

回顾ClassPathScanningCandidateComponentProvider 中的关键的代码片段如下&#xff1a;

private Set scanCandidateComponents(String basePackage) {
 //省略其他代码
 MetadataReader metadataReader   
             &#61;getMetadataReaderFactory().getMetadataReader(resource);  
   if(isCandidateComponent(metadataReader)){
       //....
   }         
}
public final MetadataReaderFactory getMetadataReaderFactory() {
   if (this.metadataReaderFactory &#61;&#61; null) {
      this.metadataReaderFactory &#61; new CachingMetadataReaderFactory();
   }
   return this.metadataReaderFactory;
}

1. 确定metadataReader

CachingMetadataReaderFactory继承自 SimpleMetadataReaderFactory&#xff0c;就是对SimpleMetadataReaderFactory加了一层缓存。

其内部的SimpleMetadataReaderFactory#getMetadataReader 为&#xff1a;

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

这里可以看出

MetadataReader metadataReader &#61;new SimpleMetadataReader(...);

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

e9eb467a373fa5dc9396dc41804f42e4.gif

AnnotationTypeFilter#matchself方法如下&#xff1a;

&#64;Override
protected boolean matchSelf(MetadataReader metadataReader) {
   AnnotationMetadata metadata &#61; metadataReader.getAnnotationMetadata();
   return metadata.hasAnnotation(this.annotationType.getName()) ||
         (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}

是metadata.hasMetaAnnotation法&#xff0c;从名称看是处理元注解&#xff0c;我们重点关注

逐步分析

找metadata.hasMetaAnnotation

metadata&#61;metadataReader.getAnnotationMetadata();

metadataReader &#61;new SimpleMetadataReader(...)

metadata&#61; new SimpleMetadataReader#getAnnotationMetadata()

//SimpleMetadataReader 的构造方法
SimpleMetadataReader(Resource resource, &#64;Nullable ClassLoader classLoader) throws IOException {
   InputStream is &#61; new BufferedInputStream(resource.getInputStream());
   ClassReader classReader;
   try {
      classReader &#61; new ClassReader(is);
   }
   catch (IllegalArgumentException ex) {
      throw new NestedIOException("ASM ClassReader failed to parse class file - " &#43;
            "probably due to a new Java class file version that isn&#39;t supported yet: " &#43; resource, ex);
   }
   finally {
      is.close();
   }

   AnnotationMetadataReadingVisitor visitor &#61;
            new AnnotationMetadataReadingVisitor(classLoader);
   classReader.accept(visitor, ClassReader.SKIP_DEBUG);

   this.annotationMetadata &#61; visitor;
   // (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
   this.classMetadata &#61; visitor;
   this.resource &#61; resource;
}

metadata&#61;new SimpleMetadataReader(...)**.**getAnnotationMetadata()&#61; new AnnotationMetadataReadingVisitor(。。)

也就是说

metadata.hasMetaAnnotation&#61;AnnotationMetadataReadingVisitor#hasMetaAnnotation

其方法如下&#xff1a;

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

逻辑很简单&#xff0c;就是判断该注解的元注解在&#xff0c;在不在metaAnnotationMap中&#xff0c;如果在就返回true。

这里面核心就是metaAnnotationMap&#xff0c;搜索AnnotationMetadataReadingVisitor类&#xff0c;没有发现赋值的地方&#xff1f;&#xff1f;&#xff01;。

查找metaAnnotationMap赋值

回到SimpleMetadataReader 的方法&#xff0c;

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

发现一个可疑的语句&#xff1a;classReader.accept。

查看accept方法

public class ClassReader {
        //省略其他代码
public void accept(..省略代码){
    //省略其他代码
    readElementValues(
    classVisitor.visitAnnotation(annotationDescriptor, /* visible &#61; */ 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 &#61; annotationOffset;
  // Read the num_element_value_pairs field (or num_values field for an array_value).
  int numElementValuePairs &#61; readUnsignedShort(currentOffset);
  currentOffset &#43;&#61; 2;
  if (named) {
    // Parse the element_value_pairs array.
    while (numElementValuePairs-- > 0) {
      String elementName &#61; readUTF8(currentOffset, charBuffer);
      currentOffset &#61;
          readElementValue(annotationVisitor, currentOffset &#43; 2, elementName, charBuffer);
    }
  } else {
    // Parse the array_value array.
    while (numElementValuePairs-- > 0) {
      currentOffset &#61;
          readElementValue(annotationVisitor, currentOffset, /* named &#61; */ null, charBuffer);
    }
  }
  if (annotationVisitor !&#61; null) {
    annotationVisitor.visitEnd();
  }
  return currentOffset;
}
}

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

确定annotationVisitor

这里的annotationVisitor&#61;AnnotationMetadataReadingVisitor#visitAnnotation

源码如下&#xff0c;注意这里传递了metaAnnotationMap&#xff01;&#xff01;

public class AnnotationMetadataReadingVisitor{
&#64;Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
   String className &#61; Type.getType(desc).getClassName();
   this.annotationSet.add(className);
   return new AnnotationAttributesReadingVisitor(
         className, this.attributesMap,
              this.metaAnnotationMap, this.classLoader);
}
}

annotationVisitor&#61;AnnotationAttributesReadingVisitor

查阅annotationVisitor.visitEnd()

annotationVisitor&#61;AnnotationAttributesReadingVisitor#visitEnd()

public class AnnotationAttributesReadingVisitor{
&#64;Override
public void visitEnd() {
   super.visitEnd();

   Class extends Annotation> annotationClass &#61; this.attributes.annotationType();
   if (annotationClass !&#61; null) {
      List attributeList &#61; this.attributesMap.get(this.annotationType);if (attributeList &#61;&#61; null) {this.attributesMap.add(this.annotationType, this.attributes);
      }else {
         attributeList.add(0, this.attributes);
      }if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationClass.getName())) {try {
            Annotation[] metaAnnotations &#61; annotationClass.getAnnotations();if (!ObjectUtils.isEmpty(metaAnnotations)) {
               Set visited &#61; new LinkedHashSet<>();for (Annotation metaAnnotation : metaAnnotations) {
                  recursivelyCollectMetaAnnotations(visited, metaAnnotation);
               }if (!visited.isEmpty()) {
                  Set metaAnnotationTypeNames &#61; 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 " &#43; annotationClass &#43; ": " &#43; ex);
            }
         }
      }
   }
}
}

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

总结

大致如下&#xff1a;

ClassPathScanningCandidateComponentProvider#findCandidateComponents

  1. 将package转化为ClassLoader类资源搜索路径packageSearchPath

  2. 加载搜素路径下的资源。

  3. isCandidateComponent 判断是否是备选组件。

内部调用的TypeFilter的match方法&#xff1a;

AnnotationTypeFilter#matchself中metadata.hasMetaAnnotation处理元注解

metadata.hasMetaAnnotation&#61;AnnotationMetadataReadingVisitor#hasMetaAnnotation

就是判断当前注解的元注解在不在metaAnnotationMap中。

AnnotationAttributesReadingVisitor#visitEnd()内部方法recursivelyCollectMetaAnnotations 递归的读取注解&#xff0c;与注解的元注解(读&#64;Service&#xff0c;再读元注解&#64;Component)&#xff0c;并设置到metaAnnotationMap

  1. 添加到返回结果的list
我是凯文cow&#xff0c;念念不忘&#xff0c;必有回响。



推荐阅读
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • Java程序设计第4周学习总结及注释应用的开发笔记
    本文由编程笔记#小编为大家整理,主要介绍了201521123087《Java程序设计》第4周学习总结相关的知识,包括注释的应用和使用类的注释与方法的注释进行注释的方法,并在Eclipse中查看。摘要内容大约为150字,提供了一定的参考价值。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了如何将CIM_DateTime解析为.Net DateTime,并分享了解析过程中可能遇到的问题和解决方法。通过使用DateTime.ParseExact方法和适当的格式字符串,可以成功解析CIM_DateTime字符串。同时还提供了关于WMI和字符串格式的相关信息。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
  • 本文介绍了一个适用于PHP应用快速接入TRX和TRC20数字资产的开发包,该开发包支持使用自有Tron区块链节点的应用场景,也支持基于Tron官方公共API服务的轻量级部署场景。提供的功能包括生成地址、验证地址、查询余额、交易转账、查询最新区块和查询交易信息等。详细信息可参考tron-php的Github地址:https://github.com/Fenguoz/tron-php。 ... [详细]
author-avatar
mobiledu2502927067
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有