源码解读:如何正确使用并区分@Resource和@Autowired注解?

环境:Spring5.3.23

源码解读:如何正确使用@Resource和@Autowired注解?

1.注解区别

@Resource 和 @Autowired 都可以用于,依赖注入。但它们之间存在一些明显的区别。

1.提供方:

  • @Autowired 是 Spring 提供的注解。
  • @Resource 是 JDK提供的注解。

2.装配方式:

  • @Autowired 默认按类型装配,即默认情况下必须要求依赖对象存在。如果要允许 null 值,可以设置它的 required 属性为
    false。如果想使用名称装配可以结合 @Qualifier 注解进行使用。
  • @Resource 默认按照名称进行装配,名称可以通过 name 属性进行指定。如果没有指定 name
    属性,当注解写在字段上时,默认取字段名进行名称查找。如果注解写在 setter 方法上默认取属性名进行装配。当找不到与名称匹配的 bean时才按照类型进行装配。

综合来看,@Resource 和 @Autowired 在提供方和装配方式上存在明显的区别。


2.源码分析

这里都将以字段注入的方式分析。

@Resource注解

该注解的处理器是CommonAnnotationBeanPostProcessor。

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
  // 获取所有使用@Resource注解的字段,InjectionMetadata包含了一个List集合
  InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
  metadata.inject(bean, beanName, pvs);
  return pvs;
}

InjectionMetadata注入核心类

/**
 * InjectionMetadata类
 * target     待注入的实例对象 
 * beanName   当前Bean的名称
 * pvs        因为是基于字段注入,所以这里没有用
 */
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
  Collection<InjectedElement> elementsToIterate = ...
  for (InjectedElement element : elementsToIterate) {
    // 注入;这里当前的element实例是ResourceElement, 这里是调用父类InjectedElement方法
    element.inject(target, beanName, pvs);
  }
}
public abstract static class InjectedElement {
  protected void inject(...) {
    // 基于字段注入
    if (this.isField) {
      Field field = (Field) this.member;
      ReflectionUtils.makeAccessible(field);
      // getResourceToInject获取实例
      field.set(target, getResourceToInject(target, requestingBeanName));
    }
  }
}
private class ResourceElement extends LookupElement {
  public ResourceElement() {
    Resource resource = ae.getAnnotation(Resource.class);
    // 是否指定了name属性;指定要注入的beanName
    String resourceName = resource.name();
    Class<?> resourceType = resource.type();
    // 如果设置了name,则为false,否则使用字段名,也就是true
    this.isDefaultName = !StringUtils.hasLength(resourceName);
    if (this.isDefaultName) {
      // 获取字段名
      resourceName = this.member.getName();
      // ...
    }
    // ...
    this.name = (resourceName != null ? resourceName : "");
    Lazy lazy = ae.getAnnotation(Lazy.class);
    // 字段上是否使用了@Lazy注解,我们这里不考虑@Lazy情况
    this.lazyLookup = (lazy != null && lazy.value());
  }
  protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
    // 字段没有添加@Lazy注解,所以为false,执行else
    return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
        // 执行这里getResource方法
        getResource(this, requestingBeanName));
  }
}

接下来进入到CommonAnnotationBeanPostProcessor中getResource方法

public class CommonAnnotationBeanPostProcessor {
  protected Object getResource(LookupElement element, @Nullable String requestingBeanName) {
    // ...
    return autowireResource(this.resourceFactory, element, requestingBeanName);  
  }
  protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName) {
    String name = element.name;
    if (factory instanceof AutowireCapableBeanFactory) {
      AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
      // 默认如果@Resource没有指定name属性,所以这里的name为字段名
      // factory.containsBean(name) 判断当前容器中是否有以该字段为名的 Bean
      // 不存在则进入;先按照名称匹配,如果不存在则进入if,if中则按照类型查找
      if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
        autowiredBeanNames = new LinkedHashSet<>();
        // 这里的逻辑就是按照当前字段类型在容器中查找Bean
        resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
        if (resource == null) {
          throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
        }
      } else {
        // 如果容器中存在字段名的Bean,则以beanName在容器中查找Bean
        resource = beanFactory.resolveBeanByName(name, descriptor);
        autowiredBeanNames = Collections.singleton(name);
      }
    }
  }
}

以上就是@Resource注解的原理


@Autowired注解

该注解的处理器是AutowiredAnnotationBeanPostProcessor。

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
  // 获取所有使用@Autowired注解的字段,InjectionMetadata包含了一个List集合
  InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
  // 进入InjectionMetadata#inject方法
  metadata.inject(bean, beanName, pvs);
  return pvs;
}
/**
 * InjectionMetadata
 * target     待注入的实例对象 
 * beanName   当前Bean的名称
 * pvs        因为是基于字段注入,所以这里没有用
 */
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
  Collection<InjectedElement> elementsToIterate = ...
  for (InjectedElement element : elementsToIterate) {
    // 注入;这里当前的element实例是AutowiredFieldElement, 这里是调用父类InjectedElement方法
    element.inject(target, beanName, pvs);
  }
}

进入AutowiredFieldElement#inject方法

private class AutowiredFieldElement {
  protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Field field = (Field) this.member;
    Object value;
    // 首次cached=false
    if (this.cached) {
      // ...
    } else {      // 解析字段获取bean对象
      value = resolveFieldValue(field, bean, beanName);
    }
    if (value != null) {
      ReflectionUtils.makeAccessible(field);
      field.set(bean, value);
    }
  }
  private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
    DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
    desc.setContainingClass(bean.getClass());
    try {
      // 进入DefaultListableBeanFactory方法
      value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
    }
  }
}

DefaultListableBeanFactory

public class DefaultListableBeanFactory {
  public Object resolveDependency(DependencyDescriptor descriptor, ...) {
    // ...
    Object result = ...
    result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
    return result
  }
  public Object doResolveDependency(DependencyDescriptor descriptor, ...) {
    // 该方法中就会按照类型进行查找相应的bean。
    // 当有多个相同类型的bean时会调用下面的方法进行处理
    if (matchingBeans.size() > 1) {
      // 存在多个相同类型时,进行处理
      autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
    }
  }
  protected String determineAutowireCandidate() {
    // 有没有@Primary注解
    String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
    if (primaryCandidate != null) {
      return primaryCandidate;
    }
    // 有没有@Priority注解,值越小,优先级越高
    String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
    if (priorityCandidate != null) {
      return priorityCandidate;
    }
    // Fallback 回退处理,如果以上情况都不存在则按照名称匹配
    for (Map.Entry<String, Object> entry : candidates.entrySet()) {
      String candidateName = entry.getKey();
      Object beanInstance = entry.getValue();
      if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
          // 按照名称匹配
          matchesBeanName(candidateName, descriptor.getDependencyName())) {
        return candidateName;
      }
    }
    return null;
  }
}

到这里你应该清楚了@Resource@Autowired注入的区别了,自身再通过源码走一遍流程,以后就不用在死记硬背这些东西了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/764578.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【论文阅读】XuanYuan: An AI-Native Database

XuanYuan: An AI-Native Database 这篇文章主要是讨论了AI4DB 和 DB4AI 集成的数据库架构&#xff0c;以此提出了AI原生的数据库&#xff0c;架构如下&#xff1a; 而具体发展阶段来说&#xff0c;AI原生数据库主要由五个阶段组成 第一阶段&#xff0c;AI建议型数据库&#xf…

MQ运行时遇到的问题

遇到的问题描述&#xff1a;我在绑定通道的时候发现了通道绑定失败&#xff0c; 原因&#xff1a; 在代码中我第一次创建交换机的时候类型的默认没有修改成topic类型的&#xff0c;导致后面的代码再去进行注册的时候并没有实现那个类型 解决&#xff1a; 更改代码&#xff0…

对不起,AI大模型不是风口

“我们正处在全新起点&#xff0c;这是一个以大模型为核心的人工智能新时代&#xff0c;大模型改变了人工智能&#xff0c;大模型即将改变世界。”——5月26日&#xff0c;百度创始人、董事长兼CEO李彦宏先生在2023中关村论坛发表了《大模型改变世界》演讲。 李彦宏指出&#…

S7---代码编译和固件下载

目录 1.代码下载 2. 工具安装 3.环境变量 4.驱动安装 5.代码编译 6.固件下载 S7和S7 Pro Gen 1音频平台 S7 Gen 1音频平台基于QCC722x蓝牙音频SoC&#xff0c;针对耳塞和其他便携式和可穿戴应用。 S7 Pro Gen 1音频平台基于QCC722x蓝牙音频SoC和QCP7321微电源Wi-Fi收发器…

Nacos2.3.x动态刷新不生效

1.日志分析 Ignore the empty nacos configuration and get it based on dataId[null.yaml] & group[DEFAULT_GROUP] Ignore the empty nacos configuration and get it based on dataId[null-local.yaml] & group[DEFAULT_GROUP] 从日志文件分析中可以得到 dataId[n…

TypeScript 中 const enum 和 enum 的核心区别在哪?日常开发应该使用哪个?

编译结果 enum 会生成一个对象&#xff0c;引用的地方保持对其引用 const enum 会擦除 enum 定义的代码&#xff0c;引用的地方会生成 inline code 使用enum&#xff1a; 使用const enum&#xff1a; PS&#xff1a;编译选项 preserveConstEnums 可以使 const enum 不去擦除 …

深度学习之半监督学习:一文梳理目标检测中的半监督学习策略

什么是半监督目标检测&#xff1f; 传统机器学习根据训练数据集中的标注情况&#xff0c;有着不同的场景&#xff0c;主要包括&#xff1a;监督学习、弱监督学习、弱半监督学习、半监督学习。由于目标检测任务的特殊性&#xff0c;在介绍半监督目标检测方法之前&#xff0c;我…

镜像私服Harbor 2.0安装-探索工厂模式:如何优化Harbor项目管理与API集成

文章目录 一、docker-compose1. 下载 Docker Compose&#xff1a;2.添加执行权限&#xff1a;3.验证安装 二、安装harbor 2.01.下载harbor离线包2. 根据需求配置 Harbor3.给harbor创建SSL证书4.预编译harbor5. 安装并启动 Harbor (必须到你安装的目录) 三、登录harbor的web页面…

哈尔滨如何选择合适的等保测评机构?

选择合适的等保测评机构确实需要细致考虑&#xff0c;您提到的八个方面已经非常全面&#xff0c;涵盖了资质、专业能力、服务质量和合规性等多个关键点。为了进一步确保所选机构的可靠性&#xff0c;还可以考虑以下几点&#xff1a; 1.技术创新与工具&#xff1a;了解测评机构是…

鸿蒙生态应用开发白皮书V3.0

来源&#xff1a;华为&#xff1a; 近期历史回顾&#xff1a;

红酒SPA:享受放松与奢华的很好结合

在繁忙的都市生活中&#xff0c;人们总是渴望找到一片宁静的天地&#xff0c;让疲惫的身心得到很好的放松。而红酒SPA&#xff0c;作为一种不同的放松方式&#xff0c;将红酒的浪漫与SPA的舒适整合&#xff0c;为现代人带来了一场奢华享受。 一、红酒的浪漫与SPA的舒适 红酒&a…

北京网站建设怎么开始做

北京作为中国的首都&#xff0c;拥有众多的企业和机构&#xff0c;网站建设不仅是一种宣传和推广的手段&#xff0c;更是企业发展的必备工具。但是对于很多企业来说&#xff0c;网站建设是一个相对陌生的领域&#xff0c;不知道从哪里开始。今天我们就来谈一谈北京网站建设的步…

算法-位图与底层运算逻辑

文章目录 1. 位图的理论基础2. 完整版位图实现3. 底层的运算逻辑-位运算 1. 位图的理论基础 首先我们要理解什么是位图, 位图的一些作用是什么 位图法就是bitmap的缩写。所谓bitmap&#xff0c;就是用每一位来存放某种状态&#xff0c;适用于大规模数据&#xff0c;但数据状态又…

【HDC.2024】探索无限可能:华为云区块链+X,创新融合新篇章

6月23日&#xff0c;华为开发者大会2024&#xff08;HDC 2024&#xff09;期间&#xff0c; “「区块链X」多元行业场景下的创新应用”分论坛在东莞松山湖举行&#xff0c;区块链技术再次成为焦点。本次论坛以"区块链X"为主题&#xff0c;集结了行业专家、技术领袖、…

fyne的MultiLineEntry设置大小

MultiLineEntry设置大小 在另一篇文章讲过&#xff0c;放入border布局中&#xff0c;可以最大化MultiLineEntry。 这里再介绍另一种方法:SetMinRowsVisible() func (e *Entry) SetMinRowsVisible(count int) {e.multiLineRows counte.Refresh() }SetMinRowsVisible强制mult…

Typora(跨平台 Markdown 编辑器 )正版值得购买吗

Typora 是一款桌面 Markdown 编辑器&#xff0c;作为国人开发的优秀软件&#xff0c;一直深受用户的喜爱。 实时预览格式 Typora 是一款适配 Windows / macOS / Linux 平台的 Markdown 编辑器&#xff0c;编辑实时预览标记格式&#xff0c;所见即所得&#xff0c;轻巧而强大…

Linux kernel 与 设备树

Linux kernel 与 设备树 1 介绍1.1 概述1.2 发展历程1.3 各版本发布时间及特色1.4 Linux 单内核1.5 Linux 内核网址1.6 NXP 官方镜像与 野火 鲁班猫镜像的区别 2 Linux 内核组成2.1 进程管理2.2 内存管理2.3 文件系统2.4 设备管理2.5 网络功能 3 Linux 内核编译3.1 编译 Kernel…

llm学习-2(使用embedding和数据处理)

首先可以简单了解一下向量数据库相关知识&#xff1a; 向量数据库相关知识&#xff08;搬运学习&#xff0c;建议还是看原文&#xff0c;这个只是我自己的学习记录&#xff09;-CSDN博客 补充&#xff1a; 使用embedding API 文心千帆API Embedding-V1是基于百度文心大模型…

【STM32】GPIO复用和映射

1.什么叫管脚复用 STM32F4有很多的内置外设&#xff0c;这些外设的外部引脚都是与GPIO复用的。也就是说&#xff0c;一个GPIO如果可以复用为内置外设的功能引脚&#xff0c;那么当这个GPIO作为内置外设使用的时候&#xff0c;就叫做复用。 STM32F4系列微控制器IO引脚通过一个…

我使用 GPT-4o 帮我挑西瓜

在 5 月 15 日&#xff0c;OpenAI 旗下的大模型 GPT-4o 已经发布&#xff0c;那时网络上已经传开&#xff0c; 但很多小伙伴始终没有看到 GPT-4o 的体验选项。 在周五的时候&#xff0c;我组建的 ChatGPT 交流群的伙伴已经发现了 GPT-4o 这个选项了&#xff0c;是在没有充值升…