@Autowired与@Resource原理知识点详解

本文详细介绍了Spring中的依赖注入原理,特别是@Autowired和@Resource的使用和实现机制。文中通过对比分析了两种注解的区别,指出@Autowired是基于类型匹配,而@Resource默认按名称匹配。此外,文章探讨了依赖注入的三种方式:属性注入、构造方法注入和setter注入,并推荐在实际开发中使用属性注入。同时,文章深入源码解释了@Autowired的自动装配过程,涉及到Bean的实例化和初始化阶段的关键步骤。
摘要由CSDN通过智能技术生成

springIOC

AOP的不多做赘述了,说下IOC:Spring IOC 解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系,而IOC有两个概念:控制反转及依赖注入。控制反转指的就是:把原有自己掌控的事交给别人去处理,更多的是一种思想或者可以理解为设计模式。 而依赖注入其实是控制反转的实现方式,通过依赖注入对象无需自行创建或者管理它的依赖关系,依赖关系将被自动注入到需要它们的对象当中去。

依赖注入的三种方式

其实还有一种基于XML的注入方式,但是现在这种方式过于繁琐且依赖冗余复杂的配置文件,再次不做讨论,仅说下常用的三种注入方式:

属性注入(字段注入)

该方式底层其实是基于Field注入的方式,也可以成为字段注入,因为注入方式实现比较简介,没有多于代码,所以这个在实际开发过程中比较常用:
在这里插入图片描述
该方式可以使用@Autowired、@Resource及@Inject来实现。后面会着重讲解这一部分。

构造方法注入

这种方式其实是spring官方比较推荐的方式,因为构造方法注入的时候会比较靠前,是在bean的实例化阶段就进行的:
在这里插入图片描述
但是有一点需要注意一下:如果只有一个构造方法的话可以不用加上@Autowired,但是如果有无参构造方法或者其他多个构造方法的话,就需要在指定的构造方法上面添加@Autowired注解了。

setter注入

我都不想列出来,但是又怕别人说我不知道这个(哈哈哈哈哈哈)。。。因为这种方式我觉得用的会越来越少的,因为这种代码太臃肿了,且好丑。。。
在这里插入图片描述

用哪个?

如果是开发的话,我建议直接属性(字段)注入完事儿,一般的业务场景基本不会有纰漏。但是如果想在bean实例化阶段进行一些处理操作或者是不在同一个IOC容器里面的话,我建议构造函数注入。 spring4以后推荐的是通过构造函数注入:

  • 依赖不可变:这个好理解,通过构造方法注入依赖,在对象创建的时候就要注入依赖,一旦对象创建成功,以后就只能使用注入的依赖而无法修改了,这就是依赖不可变(通过 set 方法注入将来还能通过 set 方法修改)。
  • 依赖不为空:通过构造方法注入的时候,会自动检查注入的对象是否为空,如果为空,则注入失败;如果不为空,才会注入成功。
  • 完全初始化:由于获取到了依赖对象(这个依赖对象是初始化之后的),并且调用了要初始化组件的构造方法,因此最终拿到的就是完全初始化的对象了。

idea里面为什么不推荐使用属性(字段)注入:

  • 对于 IOC 容器以外的环境,除了使用反射来提供它需要的依赖之外,无法复用该实现类。因为该类没有提供该属性的 set 方法或者相应的构造方法来完成该属性的初始化。换言之,要是使用属性注入,那么你这个类就只能在 IOC 容器中使用,要是想自己 new 一下这个类的对象,那么相关的依赖无法完成注入。
  • 属性注入时机较晚。在一些是实列化阶段的一些操作,无法使用注入对象。

@Autowired

在这里插入图片描述
@Autowired注解,可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作,@Autowired标注可以放在成员变量上,也可以放在成员变量的set方法上,也可以放在任意方法上表示,自动执行当前方法,如果方法有参数,会在IOC容器中自动寻找同类型参数为其传值。在上面已经讲解过了。 这里必须明确:@Autowired是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Qualifier使用。

实现原理

源码跟踪下bean的实例化及初始化,以下源码定位流程就先不截图了,等到关键重要地方再截图:

  1. SpringApplication#run()方法的this.refreshContext。
  2. AbstractApplicationContext#refresh方法的this.finishBeanFactoryInitialization(beanFactory);该方法会初始化所有非延迟加载的单例bean,包括我们在应用中声明的各种业务Bean,Bean的实例化和初始化过程逻辑都在这个函数中。
  3. 上述finishBeanFactoryInitialization方法中有this.getBean(weaverAwareName)方法。
  4. AbstractBeanFactory#doGetBean()方法中有createBean()方法。
  5. 进入AbstractAutowireCapableBeanFactory#createBean()方法,该方法内有doCreateBean()方法: doCreateBean()方法是用来构建bean的,bean的实例化及初始化逻辑都是在doCreateBean()方法里面。该方法代码截图一下吧,重点关注两个方法:
  6. 先看下applyMergedBeanDefinitionPostProcessors方法: 可以看到该方法里面调用了所有的MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法,其中有一个我们一直在找的实现类:AutowiredAnnotationBeanPostProcessor,这个就是用来处理依赖注入的后置处理器。MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法如下: AutowiredAnnotationBeanPostProcessor会对bean进行注解扫描,如果扫描到了@Autowired和@Value注解,就会把对应的方法或者属性封装起来,最终封装成InjectionMetadata对象。
  7. 我们再看第二个重要方法populateBean(),这是个非常重要的方法:填充Bean实例属性完成依赖对象的注入,此时Bean中需要依赖注入的成员已经在第六步的applyMergedBeanDefinitionPostProcessors中被对应的后置处理器(AutowiredAnnotationBeanPostProcessor)进行了存储。 这个方法内部进行部分截图: 这段代码肯定不会return,因为postProcessAfterInstantiation是为true的?重点定位到postProcessPropertyValues方法:
  8. 可以看到有两个实现类:一个就是AutowiredAnnotationBeanPostProcessor而另一个是CommonAnnotationBeanPostProcessor。@Resource是通过CommonAnnotationBeanPostProcessor的postProcessPropertyValues实现的,我们先以@Autowired为例说明下,进入到AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues方法内: 查看findAutowiringMetadata()方法: 查看下buildAutowiringMetadata()部分方法: 当调用findAutowiringMetadata()方法时,会根据autowiredAnnotationTypes这个全局变量中的元素类型来进行注解的解析,而这个全局变量在AutowiredAnnotationBeanPostProcessor的构造方法中进行了初始化: 构造方法初始化的时候会向autowiredAnnotationTypes全局集合变量里面加入@Autowired、@Inject、@Value 以上说明了findAutowiringMetadata()方法只解析出bean中带有@Autowired注解、@Inject和@Value注解的属性和方法。然后调用metadata.inject()方法,进行属性填充,继续跟踪: 以上代码中:checkedElements用于检查元素防止重复注入,bean中@Autowired 注入点会被提前解析成元信息并保存到InjectionMetadata中(对于@Resource,@LookUp等注解被解析后,也会解析成对应的InjectedElement的子类:ResourceElement、LookUpElement,但是这两个注解是在CommonAnnotationBeanPostProcessor后置处理器进行处理的),遍历InjectedElement集合继续执行注入逻辑element.inject():
  9. 该方法对应两个实现类分别为:AutowiredFieldElement及AutowiredMethodElement,字面意思对应分别为字段注入及方法注入实现,两者实现逻辑基本相同,我们以AutowiredFieldElement为例: ReflectionUtils.makeAccessible(field);利用Java的反射,为属性进行赋值,我们重点看下resolveFieldValue():
  10. 重点看下resolveDependency()方法,该方法主要调用了doResolveDependency()方法,接下来重点分析一下doResolveDependency()这个方法,源码如下:
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
		@Nullable 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值