Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >一个属性同时使用Autowired和Resource注解会发生什么?

一个属性同时使用Autowired和Resource注解会发生什么?

作者头像
编程大道
发布于 2022-07-01 07:15:23
发布于 2022-07-01 07:15:23
47200
代码可运行
举报
文章被收录于专栏:编程大道编程大道
运行总次数:0
代码可运行

如题,如果在同一个属性上使用@Autowired注解注入bean1,然后使用@Resource注解注入bean2会发生什么?

先给出几个猜想: 1.报错,不能重复注入。 2.先注入bean1再注入bean2,类似于map中put同一个key覆盖value。 3.注入bean1。Spring注入前判断属性注入过不再重复注入,且先处理@Autowired 4.注入bean2。Spring注入前判断属性注入过不再重复注入,且先处理@Resource

测试验证

首先定义一个OrderService,beanName为orderService,desc属性默认是default

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component("orderService")
public class OrderService {
   private String desc = "default";
   //getter  setter toString
}

然后使用@Bean的方式再注册一个OrderService,beanName为orderService1,desc属性赋值为update

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Bean("orderService1")
public OrderService orderService1() {
    OrderService orderService = new OrderService();
    orderService.setDesc("update");
    return orderService;
}

然后我们在UserService的orderService属性上同时加上@Autowired注解(会注入beanName为orderService的实例),和@Resource(name = "orderService1")注解,即指定注入name为orderService1的bean。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
public class UserService {
   @Autowired
   @Resource(name = "orderService1")
   private OrderService orderService;

   public void test() {
      System.out.println("test:"+orderService);
   }
}

然后我们在测试类中,从Spring容器中获取UserService,最终UserService持有的是哪个bean?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main(String[] args) {
   AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
   UserService userService = (UserService) context.getBean("userService");
   userService.test();
}

控制台输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
test:OrderService{desc='update'}

可见,UserService中 OrderService属性注入的是orderService1。调整属性上注解的顺序,控制台输出依旧如此,说明和注解顺序没有关系。这和我们的猜想2相同,即Spring先处理@Autowired注解注入orderService,再处理@Resource注解注入orderService1,从而覆盖了先注入的。我们这个结论对不对呢?往下看,从源码中找答案比较靠谱。

注入流程

在看源码之前,先看一下Spring注入流程。在Spring框架中,Bean的属性注入可以使用@Autowired注解也可以使用@Resource注解。

Autowired

@Autowired注解是Spring框架提供的,可以写在:

  • 属性上:先根据属性类型去找Bean,如果找到多个再根据属性名确定一个。
  • 构造方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个。
  • set方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个。

总体就是@Autowired先基于类型去找Bean,如果找到多个Bean,再根据name确定一个。

Resource

@Resource注解是Java提供的,可以用在方法上、属性上,它的注入流程是: 1、先判断BeanFactory中是否存在注入点名字(属性名字或方法参数名字)对应的Bean, 2、如果存在则只会根据注入点名字(属性名字或方法参数名字)去找bean,如果找不到对应的bean会报错。 3、如果不存在,再去判断@Resource注解中是否指定了name属性, 4、如果指定了,则只会根据name去找bean,如果找不到对应的bean会报错。 5、如果没有指定,则会和@Autowired注解一样,先byType再byName。

依赖注入源码分析

在Bean创建过程中,会对Bean的属性进行赋值,即依赖注入,Spring是怎么实现的呢?

我们都知道spring在创建bean的过程中有很多的扩展点,说白了就是留了很多接口,在创建bean的过程调用这些接口的方法,通过实现这些接口嵌入自定义的处理逻辑,从而完成很多功能。Spring的依赖注入也不例外。

先剧透一下,Spring中CommonAnnotationBeanPostProcessorAutowiredAnnotationBeanPostProcessor实现了InstantiationAwareBeanPostProcessorpostProcessProperties方法,完成@Resource、@Autowired的依赖注入。

如下两张图为CommonAnnotationBeanPostProcessorAutowiredAnnotationBeanPostProcessor的局部类图。

具体流程如下:

1、属性填充。

Spring实例化Bean后,调用populateBean方法对Bean进行属性填充。

AbstractAutowireCapableBeanFactorydoCreateBean方法中创建Bean实例,之后,调用同一个类中的populateBean方法做属性填充,即处理@Autowired注解和@Resource注解。

关键代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
   // 这里会调用AutowiredAnnotationBeanPostProcessor的postProcessProperties()方法,会直接给对象中的属性赋值
   // AutowiredAnnotationBeanPostProcessor内部并不会处理pvs,直接返回了
   PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
   if (pvsToUse == null) {
      if (filteredPds == null) {
         filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
      }
      pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
      if (pvsToUse == null) {
         return;
      }
   }
   pvs = pvsToUse;
}

2、属性注入。

调用InstantiationAwareBeanPostProcessorpostProcessProperties方法完成属性注入。

上述代码通过getBeanPostProcessorCache().instantiationAware获取所有InstantiationAwareBeanPostProcessor的实现类,循环并调用其postProcessProperties方法。

通过debug我们会发现,getBeanPostProcessorCache().instantiationAware获取到到的实现类CommonAnnotationBeanPostProcessor在实现类AutowiredAnnotationBeanPostProcessor的前面,如下图所示:

这就意味着Spring先处理@Resource后处理@Autowired,说明我们前面的猜想是错误的。所以,Spring是先处理@Resource的对属性注入,再处理@Autowired,发现属性已经被注入了,就不再重复注入?是不是这样我们继续看源码。 CommonAnnotationBeanPostProcessorAutowiredAnnotationBeanPostProcessorpostProcessProperties方法非常相似,AutowiredAnnotationBeanPostProcessorpostProcessProperties方法源码如下,只比CommonAnnotationBeanPostProcessor的多了一个BeanCreationException的catch。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
   // 找注入点(所有被@Autowired注解了的Field或Method)
   InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
   try {
      //注入
      metadata.inject(bean, beanName, pvs);
   }catch (BeanCreationException ex) {
      throw ex;
   }catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
   }
   return pvs;
}

findAutowiringMetadata的逻辑是从injectionMetadataCache(map,缓存注入点元数据)中获取InjectionMetadata,这里从缓存map中能够获取到InjectionMetadata,然后调用其inject方法完成属性注入。CommonAnnotationBeanPostProcessorfindAutowiringMetadata逻辑也是如此。 我们看下InjectionMetadatainject方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
   Collection<InjectedElement> checkedElements = this.checkedElements;
   Collection<InjectedElement> elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements);
   if (!elementsToIterate.isEmpty()) {
      // 遍历每个注入点进行依赖注入
      for (InjectedElement element : elementsToIterate) {
         element.inject(target, beanName, pvs);
      }
   }
}

该方法遍历注入点集合elementsToIterate,调用inject方法完成属性注入,只不过elementsToIterate的取值有点小逻辑,如果checkedElements不为null就取checkedElements,否则取injectedElements。 对于刚刚我们的猜想,会不会是elementsToIterate没有元素,所以AutowiredAnnotationBeanPostProcessor属性注入直接跳过了,所以注入的是@Resource注解的?而且,我们还没看到checkedElementsinjectedElements是在哪赋值的。

3、寻找注入点。

通过源码可以看到,实例化Bean之后,调用populateBean方法属性填充之前调了一个本类的方法applyMergedBeanDefinitionPostProcessors

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
   for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
      processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);
   }
}

CommonAnnotationBeanPostProcessorAutowiredAnnotationBeanPostProcessor也实现了相应的接口

来看看它们的实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//CommonAnnotationBeanPostProcessor
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
   super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
   InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
   metadata.checkConfigMembers(beanDefinition);
}
//AutowiredAnnotationBeanPostProcessor
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
   InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
   metadata.checkConfigMembers(beanDefinition);
}

它们都调用了findAutowiringMetadata方法寻找注入点,包装为InjectionMetadata对象并将注入点集合赋值给injectedElements属性,然后将InjectionMetadata对象放到缓存map中,所以在postProcessProperties中调用findAutowiringMetadata可以从缓存map中获取到InjectionMetadata

4、重复的注入点不会重复注入。

重复的注入点不会添加到checkedElements中。

还都调用了checkConfigMembers方法,上一个方法findAutowiringMetadata完成了injectedElements属性的赋值,那么这个方法是对checkedElements方法进行赋值的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
   Set<InjectedElement> checkedElements = new LinkedHashSet<>(this.injectedElements.size());
   for (InjectedElement element : this.injectedElements) {
      Member member = element.getMember();
      // 将Field或Method记录到BeanDefinition中的externallyManagedConfigMembers中,表示该Field或Method是BeanFactory外部管理的
      if (!beanDefinition.isExternallyManagedConfigMember(member)) {
         beanDefinition.registerExternallyManagedConfigMember(member);
         checkedElements.add(element);
      }
   }
   this.checkedElements = checkedElements;
}

这个方法的大致逻辑是将injectedElements的元素,放到checkedElements中, 只有满足条件的才放到checkedElements中,判断条件是不在RootBeanDefinition#externallyManagedConfigMembers集合中,把元素添加到该集合和checkedElements中。

由于getBeanPostProcessorCache().mergedDefinition获取到的实现类,CommonAnnotationBeanPostProcessorAutowiredAnnotationBeanPostProcessor前面。

所以对于同一个属性,第二次判断时

!beanDefinition.isExternallyManagedConfigMember(member)

条件不成立,元素就不会放到checkedElements

AutowiredAnnotationBeanPostProcessor在执行postProcessProperties方法时,调用InjectionMetadatainject方法,此时checkedElements不会有该注入点,就不会重复注入。

结论

通过源码了解到,并不是单纯的处理顺序的先后被覆盖的原因,依赖注入是创建Bean实例后完成的,通过实现MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition寻找注入点放入缓存中,再通过实现InstantiationAwareBeanPostProcessor#postProcessProperties从缓存中获取注入点,完成属性注入。其中重复的注入点不会重复放到缓存中,所以先执行的CommonAnnotationBeanPostProcessor会完成属性注入,后执行的AutowiredAnnotationBeanPostProcessor自然不会再注入。

便于理解流程,画了一张图:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 BiggerBoy 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
你所不知道的Spring的@Autowired实现细节
相信很多Java开发都遇到过一个面试题:Resource和Autowired的区别是什么?这个问题的答案相信基本都清楚,但是这两者在Spring中是如何实现的呢?这就要分析Spring源码才能知道了。友情提示:本篇主要是讲解Autowired的实现原理,不会分析Spring初始化的过程,不熟悉的读者可以先阅读笔者之前的一篇文章《这一次搞懂Spring的Bean实例化原理》。
夜勿语
2020/09/07
6100
@Autowired 到底是怎么把变量注入进来的?
在 Spring 容器中,当我们想给某一个属性注入值的时候,有多种不同的方式,例如可以通过构造器注入、可以通过 set 方法注入,也可以使用 @Autowired、@Inject、@Resource 等注解注入。
江南一点雨
2023/09/09
4670
@Autowired 到底是怎么把变量注入进来的?
谈谈 @Autowired 的实现原理
点击上方“芋道源码”,选择“设为星标” 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | Java 2021 超神之路,很肝~ 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析 网络应用框架 Netty 源码解析 消息中间件 RocketMQ 源码解析 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析 作业调度中间件 Elastic-Job 源码解析 分布式事务中间件 TCC-Transaction
芋道源码
2022/06/13
4320
谈谈 @Autowired 的实现原理
@Autowired 与 @Resource 有何不同
@Autowired来自于 spring-beans 模块;而@Resource则来自于 jakarta.annotation-api 模块,它是 Jakarta EE 规范中的内容。虽然 @Autowired 与 @Resource 均用于实现依赖注入,但 Spring 对二者的处理逻辑是不一样的。
程序猿杜小头
2023/03/05
7060
@Autowired 与 @Resource 有何不同
Spring注解@Resource源码分析
继续追踪,看metadata.inject(bean, beanName, pvs)方法
周同学
2019/10/14
1.7K0
spring源码篇(四)依赖注入(控制反转)
​ 上一篇走了一遍bean的生成过程,也是spring容器启动的一个重要过程,而在这个过程中,有一个属性填充的步骤,也就是依赖注入,这个概念不难,但其底层实现其实却有很多复杂的步骤,使得这个依赖注入的功能比较强大,所以这篇就是从源码角度,了解spring的依赖注入功能。
用针戳左手中指指头
2021/03/22
7420
@Autowired注解实现原理
在讨论代码细节之前,我们再来了解下基础知识。Spring管理可用于整个应用程序的Java对象bean。他们所在的Spring容器,被称为应用程序上下文。这意味着我们不需要处理他们的生命周期(初始化,销毁)。该任务由此容器来完成。另外,该上下文具有入口点,在Web应用程序中,是dispatcher servlet。容器(也就是该上下文)会在它那里被启动并且所有的bean都会被注入。 说的再清楚点,请看<context:annotation-config />的定义: <xsd:element name="an
JavaEdge
2018/05/16
1.8K0
Spring中的MergedBeanDefinitionPostProcessor有什么作用 ?
MergedBeanDefinitionPostProcessor这个Bean后置处理器大家可能关注的比较少,其本身也只提供了一个bean生命周期回调接口:
大忽悠爱学习
2023/05/23
1K0
Spring中的MergedBeanDefinitionPostProcessor有什么作用 ?
从源码层面带你实现一个自动注入注解
如何自己实现一个自动注入的注解 ---- 首先,需要了解到的是。Spring Bean 的生命周期 在生命周期中。注入bean属性的位置是在以下代码:populateBean 位置中 那么我们在项目中使用注解 产生一个bean的时候必定会经过以下代码进行一个bean的创建流程 /**省略代码**/ // 开始初始化 bean 实例对象 Object exposedObject = bean; try { // <5> 对 bean 进行填充,将各个属性值注入,其中,可能存在依赖于其他 bean 的属
云扬四海
2021/09/22
4210
源码剖析Spring依赖注入:今天你还不会,你就输了
在之前的讲解中,我乐意将源码拿出来并粘贴在文章中,让大家看一下。然而,我最近意识到这样做不仅会占用很多篇幅,而且实际作用很小,因为大部分人不会花太多时间去阅读源码。
努力的小雨
2024/02/06
3180
看完让你吊打面试官-@Autowired注解到底怎么实现的?
用来执行依赖注入.每当一个Spring管理的bean发现有该注解时,会直接注入相应的另一个Spring管理的bean.
JavaEdge
2020/05/27
1.4K0
看完让你吊打面试官-@Autowired注解到底怎么实现的?
Spring源码解析(七):bean后置处理器AutowiredAnnotationBeanPostProcessor
冬天vs不冷
2025/01/21
2610
Spring源码解析(七):bean后置处理器AutowiredAnnotationBeanPostProcessor
Spring源码:Bean生命周期(五)
在上一篇文章中,我们深入探讨了 Spring 框架中 Bean 的实例化过程,该过程包括从 Bean 定义中加载当前类、寻找所有实现了 InstantiationAwareBeanPostProcessor 接口的类并调用实例化前的方法、进行实例化、调用 applyMergedBeanDefinitionPostProcessors 方法等多个步骤,最终生成了一个真正的 Bean 实例。但是,这个 Bean 实例还没有被初始化和注入属性,还不能真正发挥作用。
努力的小雨
2024/05/07
1340
Spring源码解析(八):bean后置处理器CommonAnnotationBeanPostProcessor
冬天vs不冷
2025/01/21
2190
Spring源码解析(八):bean后置处理器CommonAnnotationBeanPostProcessor
一个特殊的 BeanPostProcessor
关于 BeanPostProcessor 松哥之前已经写过好几篇文章和大家聊过了,不过之前聊的都是常规的 BeanPostProcessor 玩法,还有一个特殊的 BeanPostProcessor,今天松哥来和大家梳理一下。
江南一点雨
2024/01/17
1920
一个特殊的 BeanPostProcessor
这篇文章,我们来谈一谈Spring中的属性注入
在前面的文章中已经知道了Spring是如何将一个对象创建出来的,那么紧接着,Spring就需要将这个对象变成一个真正的Bean了,这个过程主要分为两步
程序员DMZ
2020/07/14
1.8K0
@Autowired注解到底怎么实现的,你能说清楚么?
使用spring开发时,进行配置主要有两种方式,一是xml的方式,二是java config的方式。
Java小咖秀
2021/07/12
6780
不一样的视角来学习Spring源码之容器与Bean---上
Spring 的发展历史较为悠久,因此很多资料还在讲解它较旧的实现,这里出于怀旧的原因,把它们都列出来,供大家参考
大忽悠爱学习
2022/05/10
4800
不一样的视角来学习Spring源码之容器与Bean---上
注解@Autowired是如何实现的
使用spring开发时,进行配置主要有两种方式,一是xml的方式,二是java config的方式。spring技术自身也在不断的发展和改变,从当前springboot的火热程度来看,java config的应用是越来越广泛了,在使用java config的过程当中,我们不可避免的会有各种各样的注解打交道,其中,我们使用最多的注解应该就是@Autowired注解了。这个注解的功能就是为我们注入一个定义好的bean。那么,这个注解除了我们常用的属性注入方式之外还有哪些使用方式呢?它在代码层面又是怎么实现的呢?这是本篇文章着重想讨论的问题。
chengcheng222e
2021/11/04
7360
@Autowired通过源码进行原理详解
IOC,inversion of control 控制反转,有时候也叫DI,dependency injection 依赖注入,是一种代码解耦的方式。
田维常
2019/07/16
2.5K0
推荐阅读
相关推荐
你所不知道的Spring的@Autowired实现细节
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验