源码解读:如何正确使用并区分@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属性;指定要注入的beanNameString resourceName = resource.name();Class<?> resourceType = resource.type();// 如果设置了name,则为false,否则使用字段名,也就是truethis.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,执行elsereturn (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<>();// 这里的逻辑就是按照当前字段类型在容器中查找Beanresource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);if (resource == null) {throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");}} else {// 如果容器中存在字段名的Bean,则以beanName在容器中查找Beanresource = 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=falseif (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.mzph.cn/web/38498.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Labview] 改写表格内容并储存覆盖Excel

在上一个功能的基础上&#xff0c;新增表格改写保存功能 [Labview] Excel读表 & 输出表单中选中的单元格内容https://blog.csdn.net/Katrina419/article/details/140120584 Excel修改前&#xff1a; 修改保存后&#xff0c;动态改写储存Excel&#xff0c;并重新写入新的表…

[21] Opencv_CUDA应用之使用Haar级联的对象检测

Opencv_CUDA应用之使用Haar级联的对象检测 Haar级联使用矩形特征来检测对象,它使用不同大小的矩形来计算不同的线和边缘特征。矩形包含一些黑色和白色区域,如下图所示,它们在图像的不同位置居中 类Haar特征检测算法的思想是计算矩形内白色像素和黑色像素之间的差异这个方法的…

【HDC.2024】云原生中间件,构筑软件安全可信的连接桥梁

近日&#xff0c;在华为云开发者大会2024期间&#xff0c;来自华为云PaaS服务&#xff0c;中间件领域产品团队的资深专家、技术总监、高级产品经理等大咖们发表了以“云原生中间件&#xff0c;构筑软件安全可信的连接桥梁”为主题的专题演讲。 演讲伊始&#xff0c;华为云产品…

Python编程使用openai的API访问oneapi暴露的ollama qwen2大模型

首先安装conda 安装Python 3.12 &#xff08;低版本的可能缺少openai库&#xff09; conda create -n py312 python3.12 conda activate py312 然后 pip install openai 提示&#xff1a; Installing collected packages: openai Successfully installed openai-1.35.…

关于 Mybatis 的开启二级缓存返回对象不一致问题

做实验报告的时候&#xff0c;跟着学习&#xff0c;发现我已经将 开启 二级缓存的 配置都配置好了&#xff0c;但是返回值地址不一致&#xff0c;说明对象不一致&#xff0c;二级缓存命中失败。 跟着流程配置&#xff1a; mybatis-config <settings><!-- 启用 myba…

你喜欢波段交易吗?

波段交易的核心在于精准捕捉市场中的长期趋势波动&#xff0c;以实现更为稳健的收益。与剥头皮和日内交易不同&#xff0c;波段交易者更倾向于持有交易头寸数日乃至数周&#xff0c;以更宽广的视角把握市场动态。 这种交易方式的优势在于&#xff0c;它降低了对即时市场反应的…

【Gin】项目搭建 一

环境准备 首先确保自己电脑安装了Golang 开始项目 1、初始化项目 mkdir gin-hello; # 创建文件夹 cd gin-hello; # 需要到刚创建的文件夹里操作 go mod init goserver; # 初始化项目&#xff0c;项目名称&#xff1a;goserver go get -u github.com/gin-gonic/gin; # 下载…

动态规划算法,完全零基础小白教程!不是计算机的都能学会!万字吐血详解。

目录 一、动态规划算法概念 题一 1、算法解析 1&#xff09;确定状态&#xff1a; ​2&#xff09;状态转移方程&#xff1a; ​3&#xff09;初始化&#xff1a; 4&#xff09;填表顺序&#xff1a; 5&#xff09;返回值&#xff1a; 2、代码 题二 1、算法解析 1、确…

如何理解MySql的MVCC机制

MVCC是什么 MySQL的MVCC机制&#xff0c;全称为多版本并发控制&#xff08;Multi-VersionConcurrency Control&#xff09;&#xff0c;是一种提高数据库并发性能的技术。MVCC的主要目的是在保证数据一致性的同时&#xff0c;提高数据库的并发性能。 它通过为每个读操作创建数…

【高中数学/三角函数】已知:x,y皆为实数,且4x^2+y^2+xy=1 求:2x+y的最大值

【问题】 已知&#xff1a;x,y皆为实数&#xff0c;且4x^2y^2xy1 求&#xff1a;2xy的最大值 【问题来源】 https://www.ixigua.com/7289764285772497448?logTag0d228277f3a8e049ab6d 【解答】 解&#xff1a; 由4x^2y^2xy1 可得 15/4*x^21/4*x^2xyy^21 得到(15开方/…

智能版面设计:指令跟随模型在自动布局规划中的应用

在广告行业一个吸引人的视觉布局能够显著提升信息的传播效果。但对于非专业设计师来说&#xff0c;创建既美观又功能性强的布局常常是一项挑战。他们往往缺乏必要的设计技能、审美训练或资源来快速实现创意构想。传统的设计软件和在线工具虽然提供了一些模板和指导&#xff0c;…

0702_ARM6

练习&#xff1a; 中断实验 main.c #include "key.h" int main() {//初始化rcc gpiohal_key_rcc_gpio_init();//初始化extihal_key_exti_init();//初始化gichal_key_gic_init();while(1){}return 0; }key.c #include "key.h"//GPIOF初始化 void hal_key_…

Linux的一些杂项函数总结

getopt_long 解析命令行。 参考&#xff1a; C语言linux getopt_long()函数&#xff08;命令行解析&#xff09;&#xff08;getopt、getopt_long_only&#xff09;&#xff08;短选项 -&#xff0c;长选项 --&#xff09;&#xff08;option结构体&#xff09;&#xff08;opt…

vue3-openlayers marker 光晕扩散(光环扩散)(postrender 事件和 render 方法)

本篇介绍一下使用 vue3-openlayers marker 光晕扩散&#xff08;光环扩散&#xff09;&#xff08;postrender 事件和 render 方法&#xff09; 1 需求 marker 光晕扩散&#xff08;光环扩散&#xff09; 2 分析 marker 光晕扩散&#xff08;光环扩散&#xff09;使用 post…

中级java每日一道面试题-2024年7月2日

题目&#xff1a; 请解释一下 Java 中的线程安全问题&#xff0c;并提供一些常见的解决方法。 答案&#xff1a; 线程安全问题是指在多线程环境下&#xff0c;多个线程同时访问共享资源时可能出现的数据不一致或错误的情况。这可能导致程序的不可预测性和错误的结果。 常见的…

徐州三线服务器租用的优势有哪些?

对于单线服务器与双线服务器来说&#xff0c;三线服务器是能够同时拥有电信、联通和移动三条线路的服务器&#xff0c;同时也被称为三线路由器或者是三线宽带路由器&#xff0c;有着三个独立的网卡和三个IP地址&#xff0c;使用户无论是通过哪些线路连接都能够进入服务器&#…

android.bp 静态库 依赖 动态库

在Android平台上&#xff0c;使用Android.bp文件来定义和构建Android静态库&#xff08;.so文件&#xff09;和动态库&#xff08;.so文件&#xff09;之间的依赖关系是很常见的。以下是一个简单的例子&#xff0c;展示了如何在Android.bp文件中定义一个静态库&#xff0c;它依…

SPI NAND、SD NAND和eMMC对比—MK米客方德

目录 1. 容量: 2.封装类型&#xff1a; 3.速度: 4.性能: 5.寿命: 6. 使用方式: 7. 其他优缺点: 8.常见应用场景: 1. 容量: SPI NAND通常提供从几百MB到几GB的存储容量。 SD NAND的容量覆盖范围比SPI NAND更广&#xff0c;从几GB到几十GB不等。 eMMC的容量范围更大&a…

代码随想录第41天|动态规划

322. 零钱兑换 dp[j] : 最小硬币数量, j 为金额(相当于背包空间)递推公式 : dp[j] min(dp[j - coins[i]] 1, dp[j])初始化: 需要一个最大值, 避免覆盖, dp[0] 0遍历顺序: 钱币有序无序不影响, 因为求解最小个数, 结果相同(先遍历物品后背包, 先背包后物品都可) class Solut…

【chatgpt】两层gcn提取最后一层节点输出特征,如何自定义简单数据集

文章目录 两层gcn&#xff0c;提取最后一层节点输出特征&#xff0c;10个节点&#xff0c;每个节点8个特征&#xff0c;连接关系随机生成&#xff08;无全连接层&#xff09;如何计算MSE 100个样本&#xff0c;并且使用批量大小为32进行训练第一个版本定义数据集出错&#xff0…