浅析spring依赖注入方式

1、Spring实现依赖注入的方式有哪些?

这个问题似乎很简单,网上随便找一找,关于这个问题的资料便有一大堆。其中,被广泛使用的答案是:存在三种方式,有构造器注入、set方法注入、属性注入。
笔者之前也是这么认为的,但是不知道大家在使用idea开发的时候,是否有见过如下提示呢?
在这里插入图片描述
这个提示的意思是:不推荐使用属性注入。这是为什么呢?

2、什么是依赖注入

我们先来回顾下,什么是依赖注入。要提这个,就不得不也提一下控制反转。这两个是spring中最核心的功能。
控制反转是指将对象或者程序的部分控制权转移到容器或者框架中,即将对象的生命周期与对象间依赖关系交由spring容器控制。这个依赖关系主要包含其他bean、组件、配置等。
依赖注入则是指在运行时动态的将依赖关系注入到对象中。属于控制反转的一部分,由框架负责处理对象的依赖关系。(被反转的是:设置对象的依赖)

举一个简单的例子解释一下:
这是一段删除用户的Controller层代码,我把它改造了一下:

public class UserMain {
  public static void main(String[] args) {
    UserController userController = new UserController();
    userController.setUserApp(new UserApp());
    userController.deleteUser(1L);
  }
}

在main函数的内部,仅有三行代码:
第一行是为了实例化对象;
第二行则是设置该对象所需要的UserApp对象;
第三行则是执行具体的方法了。

在没有Ioc与DI之前,我们想要执行一个功能的话,需要经过如上的三步。但是有了Ioc和DI之后呢?

public class UserMain {
  public static void main(String[] args) {
    Context.get(UserController.class).deleteUser(1L);
  }
}

Spring通过Ioc控制了对象生命周期中的实例化阶段,取代了第一行代码;通过DI来注入对象的依赖关系,设置该对象所依赖的其他的对象,取代了第二行代码。所以,最终,我们只需要从Spring容器中获取到最终被装配完善的Bean,就可以直接使用了。

3、Spring推荐使用的是什么方式呢?

继续回到我们之前话题,为什么Spring官方不推荐使用属性注入呢?
相关的内容大家可以直接看下官方的文档:https://docs.spring.io/spring-framework/docs/5.3.39/reference/html/core.html#beans-factory-autowire
在这里插入图片描述
简单点讲属性注入有以下几点劣势:
1.优先级问题,会被来自构造函数注入,或者显式的手工注入覆盖;
2.无法处理基本类型
3.依赖关系不明显,尤其是对一些文档工具
4.如果存在了多个相同类型的bean,可能会注入失败(Autowired注解支持byName、byType,但是如果二者都没有找到候选bean的话,会导致最终DI失败,抛出异常)

4、为什么推荐使用构造器注入与set方法注入呢?

4.1、构造器注入的优缺点

构造器注入更加显式的指出了bean之间的依赖关系,而且强制约束了依赖的bean不能为空,支持将依赖的bean属性设置为final(这是最核心的有点:不可变性与线程安全性)。而且构造方法注入暗指了二者之间存在了强依赖关系,使用了构造器注入方式的bean,无法在依赖bean没有实例化之前就创建自身。
但是这种强依赖关系,又导致了构造器注入遇到解决循环依赖问题时,没有任何办法可以解决。
Spring解决循环依赖的方式,是将bean的暴露时间提前。将仅完成了实例化的对象就暴露出来,而不是要等一个bean完全装配完成,经过了完整的实例化、DI、初始化阶段,才暴露出来。此时的bean,内部的属性、内部依赖的组件等,可能都还没有值。但是不影响处理bean之间的依赖关系。
而这也直接导致了构造器注入会受到循环依赖的影响,因为构造器就是用来实例化bean的,所以构造器注入,相较于别的注入方式,会在实例化阶段,就更早的检验bean之间的依赖关系。因此,spring现在的处理循环依赖的措施,对构造器注入就不起效果了。
此时就需要使用set方法注入了。set方法注入生效于DI阶段,此时bean已经实例化完毕,并不会受到循环依赖的影响。

4.2、set方法注入优缺点

灵活性,允许在对象创建之后,也可以修改对象间的依赖,但是这也可能导致整个程序行为的不可控,因为无法约束实际注入的组件到底是哪个,存在被修改的可能性;
可选依赖,相较于构造函数注入,依赖项不能为空的强制约束,set方法注入允许注入一个空对象,但这同时也意味着,在使用内部的某个组件时,需要处理该组件可能为空的情况;

4.3、autowired注解的优缺点

Autowired注解的使用,过于自由、约束较少,这带来了高度的灵活性。但同样也是因为这个设计,导致了Autowired注解很容易被滥用,更容易导致bean的职责不再单一、bean之间的依赖关系不够清晰。

这其实也是我们在编码时常常需要考虑的点:灵活性是否一定越高越好呢?

5、题外话

DI阶段是在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean实现的 Autowired注解与set方法注入都是在该方法内部被处理的,分别对应了:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor,
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyPropertyValues
有兴趣的可以debug看下实际的调用流程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

崩溃的章鱼哥

请我吃个饼吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值