热门标签 | 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;必有回响。



推荐阅读
  • Hadoop MapReduce 实战案例:手机流量使用统计分析
    本文通过一个具体的Hadoop MapReduce案例,详细介绍了如何利用MapReduce框架来统计和分析手机用户的流量使用情况,包括上行和下行流量的计算以及总流量的汇总。 ... [详细]
  • Spring Security基础配置详解
    本文详细介绍了Spring Security的基础配置方法,包括如何搭建Maven多模块工程以及具体的安全配置步骤,帮助开发者更好地理解和应用这一强大的安全框架。 ... [详细]
  • Maven + Spring + MyBatis + MySQL 环境搭建与实例解析
    本文详细介绍如何使用MySQL数据库进行环境搭建,包括创建数据库表并插入示例数据。随后,逐步指导如何配置Maven项目,整合Spring框架与MyBatis,实现高效的数据访问。 ... [详细]
  • 在使用mybatis进行mapper.xml测试的时候发生必须为元素类型“mapper”声明属性“namespace”的错误项目目录结构UserMapper和UserMappe ... [详细]
  • C/C++ 应用程序的安装与卸载解决方案
    本文介绍了如何使用Inno Setup来创建C/C++应用程序的安装程序,包括自动检测并安装所需的运行库,确保应用能够顺利安装和卸载。 ... [详细]
  • 本文详细介绍了如何使用C#实现不同类型的系统服务账户(如Windows服务、计划任务和IIS应用池)的密码重置方法。 ... [详细]
  • 本文回顾了作者在求职阿里和腾讯实习生过程中,从最初的迷茫到最后成功获得Offer的心路历程。文中不仅分享了个人的面试经历,还提供了宝贵的面试准备建议和技巧。 ... [详细]
  • Asynchronous JavaScript and XML (AJAX) 的流行很大程度上得益于 Google 在其产品如 Google Suggest 和 Google Maps 中的应用。本文将深入探讨 AJAX 在 .NET 环境下的工作原理及其实现方法。 ... [详细]
  • Python3爬虫入门:pyspider的基本使用[python爬虫入门]
    Python学习网有大量免费的Python入门教程,欢迎大家来学习。本文主要通过爬取去哪儿网的旅游攻略来给大家介绍pyspid ... [详细]
  • 本文探讨了异步编程的发展历程,从最初的AJAX异步回调到现代的Promise、Generator+Co以及Async/Await等技术。文章详细分析了Promise的工作原理及其源码实现,帮助开发者更好地理解和使用这一重要工具。 ... [详细]
  • MITM(中间人攻击)原理及防范初探(二)
    上一篇文章MITM(中间人攻击)原理及防范初探(一)给大家介绍了利用ettercap进行arp欺骗及劫持明文口令,后来我发现好友rootoorotor的文章介绍比我写的更透彻,所以基础利用大家可以参看 ... [详细]
  • 本文详细介绍了如何在Oracle VM VirtualBox中实现主机与虚拟机之间的数据交换,包括安装Guest Additions增强功能,以及如何利用这些功能进行文件传输、屏幕调整等操作。 ... [详细]
  • 本文详细介绍了 `org.apache.tinkerpop.gremlin.structure.VertexProperty` 类中的 `key()` 方法,并提供了多个实际应用的代码示例。通过这些示例,读者可以更好地理解该方法在图数据库操作中的具体用途。 ... [详细]
  • 在现代Web开发中,HTML5 Canvas常用于图像处理和绘图任务。本文将详细介绍如何将Canvas中的图像导出并上传至服务器,适用于拼图、图片编辑等场景。 ... [详细]
  • 本文探讨了在多DHCP服务器环境中如何创建和管理作用域,并提出了一种有效的备用DHCP服务器方案,以确保网络服务的高可用性和稳定性。通过详细的技术分析和实践操作,本文为网络管理员提供了一套完整的解决方案,帮助其更好地应对复杂的网络环境。 ... [详细]
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社区 版权所有