0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Spring开发过程中依赖注入的几个知识点

5jek_harmonyos 来源:掘金 作者:Richard_Yi 2021-08-27 09:18 次阅读

转自丨https://juejin.cn/post/6844904056230690824

本章的内容主要是想探讨我们在进行 Spring 开发过程当中,关于依赖注入的几个知识点。感兴趣的读者可以先看下以下问题:

@Autowired,@Resource,@Inject 三个注解的区别

当你在使用@Autowired时,是否有出现过Field injection is not recommended的警告?你知道这是为什么吗?

Spring 依赖注入有哪几种方式?官方是怎么建议使用的呢?

如果你对上述问题都了解,那我个人觉得你的开发经验应该是不错的。

下面我们就依次对上述问题进行解答,并且总结知识点。

@Autowired,@Resource,@Inject 三个注解的区别

Spring 支持使用@Autowired, @Resource, @Inject 三个注解进行依赖注入。下面来介绍一下这三个注解有什么区别。

@Autowired

@Autowired为Spring 框架提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired。

这里先给出一个示例代码,方便讲解说明:

public interface Svc { void sayHello(); } @Service public class SvcA implements Svc { @Override public void sayHello() { System.out.println(“hello, this is service A”); } } @Service public class SvcB implements Svc { @Override public void sayHello() { System.out.println(“hello, this is service B”); } } @Service public class SvcC implements Svc { @Override public void sayHello() { System.out.println(“hello, this is service C”); } }

测试类:

@SpringBootTest public class SimpleTest { @Autowired // @Qualifier(“svcA”) Svc svc; @Test void rc() { Assertions.assertNotNull(svc); svc.sayHello(); } }

装配顺序:

按照type在上下文中查找匹配的bean

查找type为Svc的bean

如果有多个bean,则按照name进行匹配

如果有@Qualifier注解,则按照@Qualifier指定的name进行匹配

查找name为svcA的bean

如果没有,则按照变量名进行匹配

查找name为svc的bean

匹配不到,则报错。(@Autowired(required=false),如果设置required为false(默认为true),则注入失败时不会抛出异常)

@Inject

在 Spring 的环境下,@Inject和@Autowired 是相同的,因为它们的依赖注入都是使用AutowiredAnnotationBeanPostProcessor来处理的。

@Inject是 JSR-330 定义的规范,如果使用这种方式,切换到Guice也是可以的。

❝“Guice 是 google 开源的轻量级 DI 框架”❞

如果硬要说两个的区别,首先@Inject是 Java EE 包里的,在 SE 环境需要单独引入。另一个区别在于@Autowired可以设置required=false而@Inject并没有这个属性。

@Resource

@Resource是 JSR-250 定义的注解。Spring 在 CommonAnnotationBeanPostProcessor实现了对JSR-250的注解的处理,其中就包括@Resource。

@Resource有两个重要的属性:name和type,而Spring 将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。

装配顺序:

如果同时指定了name和type,则从 Spring 上下文中找到唯一匹配的 bean 进行装配,找不到则抛出异常。

如果指定了name,则从上下文中查找名称(id)匹配的 bean 进行装配,找不到则抛出异常。

如果指定了type,则从上下文中找到类型匹配的唯一 bean 进行装配,找不到或是找到多个,都会抛出异常。

如果既没有指定name,又没有指定type,则默认按照byName方式进行装配;如果没有匹配,按照byType进行装配。

Field injection is not recommended

在使用 IDEA 进行 Spring 开发的时候,当你在字段上面使用@Autowired注解的时候,你会发现 IDEA 会有警告提示:

❝“Field injection is not recommended Inspection info: Spring Team Recommends: “Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies”。”

翻译过来就是这个意思:

❝“不建议使用基于 field 的注入方式。Spring 开发团队建议:在你的Spring Bean 永远使用基于constructor 的方式进行依赖注入。对于必须的依赖,永远使用断言来确认。”❞

比如如下代码:

@Service public class HelpService { @Autowired @Qualifier(“svcB”) private Svc svc; public void sayHello() { svc.sayHello(); } } public interface Svc { void sayHello(); } @Service public class SvcB implements Svc { @Override public void sayHello() { System.out.println(“hello, this is service B”); } }

将光标放到@Autowired处,使用Alt + Enter 快捷进行修改之后,代码就会变成基于 Constructor 的注入方式,修改之后:

@Service public class HelpService { private final Svc svc; @Autowired public HelpService(@Qualifier(“svcB”) Svc svc) { // Assert.notNull(svc, “svc must not be null”); this.svc = svc; } public void sayHello() { svc.sayHello(); } }

如果按照 Spring 团队的建议,如果svc是必须的依赖,应该使用Assert.notNull(svc, “svc must not be null”)来确认。

修正这个警告提示固然简单,但是我觉得更重要是去理解为什么 Spring 团队会提出这样的建议?直接使用这种基于 field 的注入方式有什么问题?

首先我们需要知道,Spring 中有这么3种依赖注入的方式:

基于 field 注入(属性注入)

基于 setter 注入

基于 constructor 注入(构造器注入)

1. 基于 field 注入

所谓基于 field 注入,就是在bean的变量上使用注解进行依赖注入。本质上是通过反射的方式直接注入到 field。这是我平常开发中看的最多也是最熟悉的一种方式,同时,也正是 Spring 团队所不推荐的方式。比如:

@Autowired private Svc svc;

2. 基于 setter 方法注入

通过对应变量的setXXX()方法以及在方法上面使用注解,来完成依赖注入。比如:

private Helper helper; @Autowired public void setHelper(Helper helper) { this.helper = helper; }

❝“注:在 Spring 4.3 及以后的版本中,setter 上面的 @Autowired 注解是可以不写的。”

❞3. 基于 constructor 注入

将各个必需的依赖全部放在带有注解构造方法的参数中,并在构造方法中完成对应变量的初始化,这种方式,就是基于构造方法的注入。比如:

private final Svc svc; @Autowired public HelpService(@Qualifier(“svcB”) Svc svc) { this.svc = svc; }

❝“在 Spring 4.3 及以后的版本中,如果这个类只有一个构造方法,那么这个构造方法上面也可以不写 @Autowired 注解。”

❞基于 field 注入的好处正如你所见,这种方式非常的简洁,代码看起来很简单,通俗易懂。你的类可以专注于业务而不被依赖注入所污染。你只需要把@Autowired扔到变量之上就好了,不需要特殊的构造器或者set方法,依赖注入容器会提供你所需的依赖。

基于 field 注入的坏处❝“成也萧何败也萧何”❞

基于 field 注入虽然简单,但是却会引发很多的问题。这些问题在我平常开发阅读项目代码的时候就经常遇见。

容易违背了单一职责原则 使用这种基于 field 注入的方式,添加依赖是很简单的,就算你的类中有十几个依赖你可能都觉得没有什么问题,普通的开发者很可能会无意识地给一个类添加很多的依赖。

但是当使用构造器方式注入,到了某个特定的点,构造器中的参数变得太多以至于很明显地发现 something is wrong。拥有太多的依赖通常意味着你的类要承担更多的责任,明显违背了单一职责原则(SRP:Single responsibility principle)。❝“这个问题在我司的项目代码真的很常见。”❞

依赖注入与容器本身耦合依赖注入框架的核心思想之一就是受容器管理的类不应该去依赖容器所使用的依赖。换句话说,这个类应该是一个简单的 POJO(Plain Ordinary Java Object)能够被单独实例化并且你也能为它提供它所需的依赖。这个问题具体可以表现在:

你的类不能绕过反射(例如单元测试的时候)进行实例化,必须通过依赖容器才能实例化,这更像是集成测试

你的类和依赖容器强耦合,不能在容器外使用

不能使用属性注入的方式构建不可变对象(final 修饰的变量)

Spring 开发团队的建议❝“Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies.”❞简单来说,就是

强制依赖就用构造器方式

可选、可变的依赖就用 setter 注入当然你可以在同一个类中使用这两种方法。构造器注入更适合强制性的注入旨在不变性,Setter 注入更适合可变性的注入。

让我们看看 Spring 这样推荐的理由,首先是基于构造方法注入,❝“The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.”❞Spring 团队提倡使用基于构造方法的注入,因为这样一方面可以将依赖注入到一个不可变的变量中 (注:final 修饰的变量),另一方面也可以保证这些变量的值不会是 null。此外,经过构造方法完成依赖注入的组件 (注:比如各个 service),在被调用时可以保证它们都完全准备好了。

与此同时,从代码质量的角度来看,一个巨大的构造方法通常代表着出现了代码异味,这个类可能承担了过多的责任。

而对于基于 setter 的注入,他们是这么说的:❝“Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later.”❞

基于 setter 的注入,则只应该被用于注入非必需的依赖,同时在类中应该对这个依赖提供一个合理的默认值。如果使用 setter 注入必需的依赖,那么将会有过多的 null 检查充斥在代码中。使用 setter 注入的一个优点是,这个依赖可以很方便的被改变或者重新注入。

小结

以上就是本文的所有内容,希望阅读本文之后能让你对 Spring 的依赖注入有更深的理解。

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 开发
    +关注

    关注

    0

    文章

    366

    浏览量

    40811
  • spring
    +关注

    关注

    0

    文章

    338

    浏览量

    14311
  • 智能构造器
    +关注

    关注

    0

    文章

    2

    浏览量

    5579

原文标题:Spring官方为什么建议构造器注入?

文章出处:【微信号:harmonyos_developer,微信公众号:harmonyos_developer】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    接口测试理论、疑问收录与扩展相关知识点

    本文章使用王者荣耀游戏接口、企业微信接口的展示结合理论知识,讲解什么是接口测试、接口测试理论、疑问收录与扩展相关知识点知识学院,快来一起看看吧~
    的头像 发表于 11-15 09:12 202次阅读
    接口测试理论、疑问收录与扩展相关<b class='flag-5'>知识点</b>

    自己做的TAS5825板子软件开发过程中怎么接到PPC3上去调试呢?

    我们打算做一个TAS5825的板子,PPC3软件已经申请下来了,有个问题是我们自己做的TAS5825板子软件开发过程中怎么接到PPC3上去调试呢?
    发表于 10-15 08:15

    模拟电子技术知识点问题总结概览

    给大家分享模拟电子技术知识点问题总结。
    的头像 发表于 05-08 15:16 1098次阅读
    模拟电子技术<b class='flag-5'>知识点</b>问题总结概览

    FPGA开发过程中配置全局时钟需要注意哪些问题

    在FPGA开发过程中,配置全局时钟是一个至关重要的步骤,它直接影响到整个系统的时序和性能。以下是配置全局时钟时需要注意的一些关键问题: 时钟抖动和延迟 :全局时钟资源的设计目标是实现最低的时钟抖动
    发表于 04-28 09:43

    一篇搞定DCS系统相关知识点

    目标。DCS系统广泛应用于各个行业,如化工、电力、制药等。在这些行业,DCS系统可以实现对生产过程的集中监控和分散控制,提高生产效率和产品质量,降低能耗和减少环境污染,从而保证产品质量,并确保生产过程的安全可靠。 二.DCS系
    的头像 发表于 03-26 18:40 827次阅读
    一篇搞定DCS系统相关<b class='flag-5'>知识点</b>

    【量子计算机重构未来 | 阅读体验】第二章关键知识点

    本帖最后由 oxlm_1 于 2024-3-6 23:20 编辑 之所以将第二章单独拿出来,是因为在阅读过程中,发现第二章知识点较多,理解起来比较耗时间。 第二章的主要知识点: 量子
    发表于 03-06 23:17

    Stages—研发过程可视化建模和管理平台

    、ISO26262等标准。Stages聚焦于研发过程的用户体验,允许用户集中访问过程描述信息、项目文档、模板、实践或者技术知识库。在Stages定义好的
    的头像 发表于 02-05 14:36 363次阅读
    Stages—研<b class='flag-5'>发过程</b>可视化建模和管理平台

    MCU在运行过程中,可以调整它的主频吗?

    希望MCU在运行过程中,可以调整它的主频,比如说,在30MHz/55MHz/140MHz,这几个之间切换。 但不希望重启或者复位mcu。 可以实现吗?
    发表于 01-16 07:39

    在BF707开发过程中向Flash烧写一段代码,然后断电进行加载,发现并未加载成功如何解决?

    在BF707开发过程中向Flash烧写过一段代码,然后断电进行加载,发现并未加载成功,当进行如下操作却失败】 1.利用CCES仿真器,在debug情况下对JTAG进行Test结果为OK的,但当
    发表于 01-12 06:03

    Spring事务传播性的相关知识

    本文主要介绍了Spring事务传播性的相关知识
    的头像 发表于 01-10 09:29 406次阅读
    <b class='flag-5'>Spring</b>事务传播性的相关<b class='flag-5'>知识</b>

    浅谈初级电工必备知识点

    对于初学电工的朋友来说,掌握一些基础且实用的知识点是非常重要的。本文旨在分享初级电工应该掌握的核心知识,帮助新手电工更好地入门和提升技能。
    的头像 发表于 12-26 10:44 1035次阅读

    ASIC芯片开发过程

    电子发烧友网站提供《ASIC芯片开发过程.ppt》资料免费下载
    发表于 12-25 10:04 1次下载

    TCP协议面试常问知识点总结

    TCP 作为传输层的协议,是一个IT工程师素养的体现,也是面试中经常被问到的知识点。在此,我将 TCP 核心的一些问题梳理了一下,希望能帮到各位。
    的头像 发表于 12-15 10:38 774次阅读
    TCP协议面试常问<b class='flag-5'>知识点</b>总结

    开关模式下的电源电流如何检测?这12个电路&amp;10个知识点讲明白了

    开关模式下的电源电流如何检测?这12个电路&10个知识点讲明白了
    的头像 发表于 12-06 16:04 767次阅读
    开关模式下的电源电流如何检测?这12个电路&amp;10个<b class='flag-5'>知识点</b>讲明白了

    Spring依赖注入的四种方式

    Spring框架依赖注入是一种核心的概念和机制。通过依赖注入,我们可以让对象之间的
    的头像 发表于 12-03 15:11 1902次阅读