【Spring】浅谈spring为什么推荐使用构造器注入

目录

一、前言
二、常见的三种注入方式
2.1 field注入
2.2 构造器注入
2.3 setter注入
三、构造器注入的好处
四、答疑
五、总结

一、前言

​ Spring框架对Java开发的重要性不言而喻,其核心特性就是IOC(Inversion of Control, 控制反转)和AOP,平时使用最多的就是其中的IOC,我们通过将组件交由Spring的IOC容器管理,将对象的依赖关系由Spring控制,避免硬编码所造成的过度程序耦合。前几天的时候,笔者的同事问我为什么要使用构造器的注入方式,我回答说因为Spring文档推荐这种,而说不出为什么 T^T,后面抽时间了解了一下,下面就是笔者要讨论的就是其注入方式。
 

二、常见的三种注入方式

​ 笔者为了方便起见就只是用注解的方式注入(现在也很少使用xml了吧,(~ ̄▽ ̄)~)

2.1 field注入

@Controller
public class FooController {@Autowired//@Injectprivate FooService fooService;//简单的使用例子,下同public List<Foo> listFoo() {return fooService.list();}
}

这种注入方式应该是笔者目前为止开发中见到的最常见的注入方式。原因很简单:

  1. 注入方式非常简单:加入要注入的字段,附上@Autowired,即可完成。
  2. 使得整体代码简洁明了,看起来美观大方。
     

2.2 构造器注入

@Controller
public class FooController {private final FooService fooService;@Autowiredpublic FooController(FooService fooService) {this.fooService = fooService;}//使用方式上同,略
}

​ 在Spring4.x版本中推荐的注入方式就是这种,相较于上面的field注入方式而言,就显得有点难看,特别是当注入的依赖很多(5个以上)的时候,就会明显的发现代码显得很臃肿。对于从field注入转过来+有强迫症的园友 来说,简直可以说是石乐志  (`Д´*)9。对于这一点我们后面再来讨论,别急。
 

2.3 setter注入

@Controller
public class FooController {private FooService fooService;//使用方式上同,略@Autowiredpublic void setFooService(FooService fooService) {this.fooService = fooService;}
}

​ 在Spring3.x刚推出的时候,推荐使用注入的就是这种,笔者现在也基本没看到过这种注解方式,写起来麻烦,当初推荐Spring自然也有他的道理,这里我们引用一下Spring当时的原话:

The Spring team generally advocates setter injection, because large numbers of constructor arguments can get unwieldy, especially when properties are optional. Setter methods also make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is a compelling use case.

Some purists favor constructor-based injection. Supplying all object dependencies means that the object is always returned to client (calling) code in a totally initialized state. The disadvantage is that the object becomes less amenable to reconfiguration and re-injection.

​ 咳咳,简单的翻译一下就是:构造器注入参数太多了,显得很笨重,另外setter的方式能用让类在之后重新配置或者重新注入
 

​ 那么后面为什么又换成构造器注入了呢?(喂喂喂,Spring你前一大版本还贬低构造器注入,后面就立刻捧人家了不好吧,不过能用于承认自己的错误,才是真正令人称赞的地方吧 (๑•̀ㅂ•́)و✧)
 

三、构造器注入的好处

​ 先来看看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.

​ 咳咳,再来简单的翻译一下:这个构造器注入的方式啊,能够保证注入的组件不可变,并且确保需要的依赖不为空。此外,构造器注入的依赖总是能够在返回客户端(组件)代码的时候保证完全初始化的状态
 

下面来简单的解释一下:

  • 依赖不可变:其实说的就是final关键字,这里不再多解释了。不明白的园友可以回去看看Java语法。
  • 依赖不为空(省去了我们对其检查):当要实例化FooController的时候,由于自己实现了有参数的构造函数,所以不会调用默认构造函数,那么就需要Spring容器传入所需要的参数,所以就两种情况:1、有该类型的参数->传入,OK 。2:无该类型的参数->报错。所以保证不会为空,Spring总不至于传一个null进去吧 😦
  • 完全初始化的状态:这个可以跟上面的依赖不为空结合起来,向构造器传参之前,要确保注入的内容不为空,那么肯定要调用依赖组件的构造方法完成实例化。而在Java类加载实例化的过程中,构造方法是最后一步(之前如果有父类先初始化父类,然后自己的成员变量,最后才是构造方法,这里不详细展开。)。所以返回来的都是初始化之后的状态。

等等,比较完了setter注入与构造器注入的优缺点,你还没用说使用field注入与构造器的比较呢!那么我们再回头看一看使用最多的field注入方式:

//承接上面field注入的代码,假如客户端代码使用下面的调用(或者再Junit测试中使用)
//这里只是模拟一下,正常来说我们只会暴露接口给客户端,不会暴露实现。
FooController fooController = new FooController();
fooController.listFoo(); // -> NullPointerException

如果使用field注入,缺点显而易见,对于IOC容器以外的环境,除了使用反射来提供它需要的依赖之外,无法复用该实现类。而且将一直是个潜在的隐患,因为你不调用将一直无法发现NPE的存在。

还值得一提另外一点是:使用field注入可能会导致循环依赖,即A里面注入B,B里面又注入A:

public class A {@Autowiredprivate B b;
}public class B {@Autowiredprivate A a;
}

如果使用构造器注入,在spring项目启动的时候,就会抛出:BeanCurrentlyInCreationException:Requested bean is currently in creation: Is there an unresolvable circular reference?从而提醒你避免循环依赖,如果是field注入的话,启动的时候不会报错,在使用那个bean的时候才会报错


四、答疑

​ 好了,相信已经园友们知道了构造器注入的好处,那么回到了在前面提到的问题:

Q1:跟3.x里说的一样,我要是有大量的依赖要注入,构造方法不会显得很臃肿吗?

对于这个问题,说明你的类当中有太多的责任,那么你要好好想一想是不是自己违反了类的单一性职责原则,从而导致有这么多的依赖要注入。

Q2:是不是其他的注入方式都不适合用了呢?

当然不是,存在即是合理!setter的方式既然一开始被Spring推荐肯定是有它的道理,像之前提到的setter的方式能用让类在之后重新配置或者重新注入,就是其优点之一。除此之外,如果一个依赖有多种实现方式,我们可以使用@Qualifier ,在构造方法里选择对应的名字注入,也可以使用field或者setter的方式来手动配置要注入的实现。
 

五、总结

​ 使用构造器注入的好处:

  1. 保证依赖不可变(final关键字)
  2. 保证依赖不为空(省去了我们对其检查)
  3. 保证返回客户端(调用)的代码的时候是完全初始化的状态
  4. 避免了循环依赖
  5. 提升了代码的可复用性

另外,当有一个依赖有多个实现的使用,推荐使用field注入或者setter注入的方式来指定注入的类型。这是spring官方博客对setter注入方式和构造器注入的比较。谢谢各位园友观看,如果有描述不对的地方欢迎指正,与大家共同进步!

参考链接:

Setter injection versus constructor injection and the use of @Required

Field Dependency Injection Considered Harmful | Vojtech Ruzicka's Programming Blog

 

作者:joemsu

出处:joemsu - 博客园

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/36038.shtml

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

相关文章

RunLoop

1.CFRunLoopModeRef特征代表RunLoop对象内的运行模式(每个RunLoop对象内存中存在很多种运行模式,每个Mode运行模式下必然包含若干个有效的Source0/Source1/Timer/Observer数据序组) 2.RunLoop对象活跃(操作)启动时能且仅能选择某个Mode匹配currentMode(暗示Loop对象的操作运行必…

分类预测 | MATLAB实现BO-BiGRU贝叶斯优化双向门控循环单元多输入分类预测

分类预测 | MATLAB实现BO-BiGRU贝叶斯优化双向门控循环单元多输入分类预测 目录 分类预测 | MATLAB实现BO-BiGRU贝叶斯优化双向门控循环单元多输入分类预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.Matlab实现BO-BiGRU贝叶斯优化双向门控循环单元多特征分…

2.1.2 VisionOS——VisionOS 中的窗口化应用程序

在visionOS中&#xff0c;用户可以使用窗口来呈现2D或3D内容&#xff0c;或者使用体积来呈现3D内容和对象。Unity 将这些窗口中的应用程序描述为“窗口应用程序”。 默认情况下&#xff0c;如果您构建针对visionOS 平台的Unity 应用程序而未通过XR 插件管理器启用PolySpatial …

React - useEffect函数的理解和使用

文章目录 一&#xff0c;useEffect描述二&#xff0c;它的执行时机三&#xff0c;useEffect分情况使用1&#xff0c;不写第二个参数 说明监测所有state&#xff0c;其中一个变化就会触发此函数2&#xff0c;第二个参数如果是[]空数组&#xff0c;说明谁也不监测3&#xff0c;第…

gRPC vs REST:创建API的方法比较

本文对gRPC和REST的特征和区别进行了介绍&#xff0c;这可能是当今创建API最常用的两种方法。 文章目录 一、gRPC的介绍 二、什么是REST&#xff1f; 三、什么是gRPC? 四、gRPC和REST的比较 &#xff08;1&#xff09;底层HTTP协议 &#xff08;2&#xff09;支持的数据…

平替 Docker - 玩转容器新利器 Podman Desktop (视频)

《OpenShift 4.x HOL教程汇总》 在 podman-desktop 1.2.1 podman 4.4 环境中验证。 文章目录 什么是 podman 和 podman-desktop安装 podman 和 podman-desktop 基本环境Image、Container 和 Pod 的基本操作拉取 Image运行 Container 将 Pod 部署到 Kubernetes安装 Kind 扩展插…

Python爬虫——selenium_元素定位

元素定位&#xff1a;自动化要做的就是模拟鼠标和键盘来操作这些元素&#xff0c;点击&#xff0c;输入等等。操作这些元素前首先要找到它们&#xff0c;WebDriver提供很多定位元素的方法 from selenium import webdriver# 创建浏览器对象 path files/chromedriver.exe brows…

【安全】浅谈信息安全

信息安全 理解信息安全&#xff0c;要从“信息”、“安全”两个角度入手。 信息 信息是对客观世界的反映&#xff0c;表现客观事物的运动状态和变化的实质内容。 信息具有可识别、可传载、可共享、可度量的基本特征。 信息系统 信息系统是获取&#xff08;收集&#xff0…

中心对称链表

文章目录 1 题目2 思路2.1 思路一2.2 思路二2.3 考点2.4 扩展 3 实现3.1 思路13.2 思路23.3 完整例子 1 题目 已知长度为n&#xff08;n>1&#xff09;的单链表&#xff0c;表头指针为L&#xff0c;结点结构由data和next两个域构成&#xff0c;其中data域为字符型&#xff…

Linux RPM包安装、卸载和升级(rpm命令)详解

(转载请删除括号里的内容) 下面讲解一下&#xff0c;如何使用 rpm 命令对 RPM 二进制包进行安装、卸载和升级操作。我们以安装 apache 程序为例。 RPM包默认安装路径 通常情况下&#xff0c;RPM 包采用系统默认的安装路径&#xff0c;所有安装文件会按照类别分散安装到下表所…

优漫动游 大厂需要什么样的ui设计师呢?

通常来说大公司UI设计的流程主要是这样的&#xff1a;创意-头脑风暴-策划方案-交互设计&评审-美术设计&评审-开发实施&#xff0c;不过实际上大多数公司都有自己的一套流程&#xff0c;源于公司的基因、公司组织体系、公司领导风格。一起了解大厂需要什么样的ui设计师呢…

谷粒商城第十一天-品牌管理中关联分类

目录 一、总述 二、前端部分 1. 调整查询调用 2. 关联分类 三、后端部分 四、总结 一、总述 之前是在商品的分类管理中直接使用的若依的逆向代码 有下面的几个问题&#xff1a; 1. 表格上面的参数填写之后&#xff0c;都是按照完全匹配进行搜索&#xff0c;没有模糊匹配…

nodejs实现前后端websocket通信+心跳示例

nodejs后端代码 server.js //需要安装ws模块 npm install ws const WebSocket require("ws") const port 8085const ws new WebSocket.Server({port})ws.on("connection", (socket) > {socket.on("message",(message) > {const da…

自定义hook之首页数据请求动作封装 hooks

本例子实现了自定义hook之首页数据请求动作封装 hooks&#xff0c;具体代码如下 export type OrganData {dis: Array<{ disease: string; id: number }>;is_delete: number;name: string;organ_id: number;parent_id: number;sort: number; }; export type SwiperData …

【STM32】简介

&#x1f6a9; WRITE IN FRONT &#x1f6a9; &#x1f50e; 介绍&#xff1a;"謓泽"正在路上朝着"攻城狮"方向"前进四" &#x1f50e;&#x1f3c5; 荣誉&#xff1a;2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2022博客之星T…

(2)linux虚拟机配置中文输入法和如何下载软件

&#xff08;一&#xff09;配置中文输入法&#xff1a; 1、sudo apt-get install fcitx&#xff0c;安装fcitx框架&#xff0c;安装完成之后&#xff0c;选择该框架 2、接下来输入sudo apt-get install fcitx fcitx-googlepinyin&#xff0c;安装谷歌输入法之后&#xff0c;重…

WebSocket与消息推送

B/S结构的软件项目中有时客户端需要实时的获得服务器消息&#xff0c;但默认HTTP协议只支持请求响应模式&#xff0c;这样做可以简化Web服务器&#xff0c;减少服务器的负担&#xff0c;加快响应速度&#xff0c;因为服务器不需要与客户端长时间建立一个通信链接&#xff0c;但…

Windows - UWP - 网络不好的情况下安装(微软商店)MicrosoftStore的应用

Windows - UWP - 网络不好的情况下安装&#xff08;微软商店&#xff09;MicrosoftStore的应用 前言 UWP虽然几乎被微软抛弃了&#xff0c;但不得不否认UWP应用给用户带来的体验。沙箱的运行方式加上微软的审核&#xff0c;用户使用起来非常放心&#xff0c;并且完美契合Wind…

聚类与回归

聚类 聚类属于非监督式学习&#xff08;无监督学习&#xff09;&#xff0c;往往不知道因变量。 通过观察学习&#xff0c;将数据分割成多个簇。 回归 回归属于监督式学习&#xff08;有监督学习&#xff09;&#xff0c;知道因变量。 通过有标签样本的学习分类器 聚类和…

前端实现文件预览功能

前端实现文件预览功能 ❝ 需求&#xff1a;实现一个在线预览pdf、excel、word、图片等文件的功能。 介绍&#xff1a;支持pdf、xlsx、docx、jpg、png、jpeg。 以下使用Vue3代码实现所有功能&#xff0c;建议以下的预览文件标签可以在外层包裹一层弹窗。 ❞ 图片预览 iframe标签…