网易首页 > 网易号 > 正文 申请入驻

今天的面试官是个锤子,Spring为什么建议使用构造器来注入?

0
分享至

推荐大家关注一个公众号

后台回复“大礼包”有惊喜礼包!

日英文

One of the best things in life is seeing a smile on a person's face and knowing that you put it there.

生活中最美好的一件事情是,因为你,某个人脸上洋溢着微笑。

每日掏心话

平淡是心静如水,是人生的一道风景,是平平静静,是一种境界,一种状态,一种心情。

责编:乐乐 | 来自:Richard_Yi 链接:juejin.cn/post/6844904056230690824

往日回顾:

正文

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

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

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

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

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

下面我们就依次对上述问题进行解答,并且总结知识点。如果您正在学习Spring Boot,推荐一个连载多年还在继续更新的免费教程:http://blog.didispace.com/spring-boot-learning-2x/

@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();
}

}

装配顺序:


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

    查找type为Svc的bean


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



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

      查找name为svcA的bean


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

      查找name为svc的bean


  3. 匹配不到,则报错。(@Autowired(required=false),如果设置requiredfalse(默认为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有两个重要的属性:nametype,而Spring 将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。

装配顺序:

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

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

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

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

另外,如果您正在学习Spring Cloud,推荐一个连载多年还在继续更新的免费教程:https://blog.didispace.com/spring-cloud-learning/

IDEA 提示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;@Autowiredpublic 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 的依赖注入有更深的理解。

你还有什么想要补充的吗?

PS:欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,欢迎转发分享给更多人。


版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢!

欢迎加入后端架构师,在后台回复“”即可。

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。在这里,我为大家准备了一份2021年最新最全BAT等大厂Java面试经验总结。

别找了,想获取史上最简单的Java大厂面试题学习资料

扫下方二维码回复「面试」就好了

嘿,你在看吗

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
对中国毫无认同感!38万苗族人挤进美国,背叛与鲜血酿出“恶果”

对中国毫无认同感!38万苗族人挤进美国,背叛与鲜血酿出“恶果”

青栀伊人
2024-06-17 22:11:27
店员泼顾客咖啡后续:知情人透露事情起因,其实真怪不得小姑娘!

店员泼顾客咖啡后续:知情人透露事情起因,其实真怪不得小姑娘!

起喜电影
2024-06-20 16:49:20
严查腐败作风问题 湖北一地追责问责27人

严查腐败作风问题 湖北一地追责问责27人

鲁中晨报
2024-06-20 11:23:07
西南石油大学迎来新任副校长

西南石油大学迎来新任副校长

鲁中晨报
2024-06-20 17:11:10
中国逆袭!ASML震惊:近百台光刻机横空出世,谁主沉浮?

中国逆袭!ASML震惊:近百台光刻机横空出世,谁主沉浮?

嘿哥哥科技
2024-06-20 15:40:06
英格兰队内讧!索斯盖特调停!短短10字,皇马1.8亿巨星让位

英格兰队内讧!索斯盖特调停!短短10字,皇马1.8亿巨星让位

阿泰希特
2024-06-20 14:14:56
1775名士兵当场被消灭 数百件重型装备直接炸成废铁:现场惨烈!

1775名士兵当场被消灭 数百件重型装备直接炸成废铁:现场惨烈!

聚峰军评
2024-06-18 08:38:30
插手司法活动,上海市奉贤区委统战部原部长周龙华被公诉!

插手司法活动,上海市奉贤区委统战部原部长周龙华被公诉!

正义网
2024-06-19 16:25:13
内蒙古发生特大杀人案,矮个凶手连杀5人,当地百姓透露杀人原因

内蒙古发生特大杀人案,矮个凶手连杀5人,当地百姓透露杀人原因

法制社会报
2024-06-20 09:20:02
外交部:按照中外大熊猫合作协议规定,将在今年有序接返协议到期和幼崽到龄的10余只大熊猫

外交部:按照中外大熊猫合作协议规定,将在今年有序接返协议到期和幼崽到龄的10余只大熊猫

环球网资讯
2024-06-20 16:08:29
黄一鸣:我希望女儿继承王思聪的财产,但他耍赖不给也无所谓

黄一鸣:我希望女儿继承王思聪的财产,但他耍赖不给也无所谓

映射生活的身影
2024-06-19 20:26:53
广西柳州市委原书记郑俊康被开除党籍

广西柳州市委原书记郑俊康被开除党籍

界面新闻
2024-06-20 13:11:29
黄一鸣“杀疯了”!直播间卖大葱养孩子,王思聪被整得完全没脾气

黄一鸣“杀疯了”!直播间卖大葱养孩子,王思聪被整得完全没脾气

萌神木木
2024-06-18 21:18:32
证券市场传来2大消息,周五A股将迎来惊涛骇浪,这个板块倒车接人

证券市场传来2大消息,周五A股将迎来惊涛骇浪,这个板块倒车接人

一丛深色花儿
2024-06-20 16:40:58
姜萍是否找人代考?达摩院澄清对姜萍很不利,浙大欲言又止

姜萍是否找人代考?达摩院澄清对姜萍很不利,浙大欲言又止

平老师666
2024-06-19 20:59:10
72小时内,中俄双方都有行动,中方发起反制,普京强硬表态

72小时内,中俄双方都有行动,中方发起反制,普京强硬表态

国学璀璨
2024-06-19 17:09:36
朝阳区平房乡举办文化节迎“七一”

朝阳区平房乡举办文化节迎“七一”

北青网-北京青年报
2024-06-19 11:09:05
北大硕士愿出500w证明姜萍作假:如果输了,愿意支付她未来学费

北大硕士愿出500w证明姜萍作假:如果输了,愿意支付她未来学费

熙熙说教
2024-06-19 15:53:51
北交所IPO|扬德环能提交注册超7个月仍未获批,关联方与子公司或经营混同,内控有效性屡遭问询

北交所IPO|扬德环能提交注册超7个月仍未获批,关联方与子公司或经营混同,内控有效性屡遭问询

时代商学院
2024-06-20 10:01:20
北大硕士赵斌:姜萍连题目都看不懂,点名王润秋,说错愿承担后果

北大硕士赵斌:姜萍连题目都看不懂,点名王润秋,说错愿承担后果

东东趣谈
2024-06-18 17:25:07
2024-06-20 19:00:49
程序员小乐
程序员小乐
有趣有内涵
3163文章数 9493关注度
往期回顾 全部

科技要闻

小米SU7流量泼天,富贵却被蔚来接住了

头条要闻

女大学生称按摩时遭男技师扒内裤 警方初步判断是擦边

头条要闻

女大学生称按摩时遭男技师扒内裤 警方初步判断是擦边

体育要闻

绿军的真老大,开始备战下赛季了

娱乐要闻

叶舒华参加柯震东生日聚会,五毒俱全

财经要闻

深圳一网红学位房14万/平跌到4万/平

汽车要闻

售价11.79-14.39万元 新一代哈弗H6正式上市

态度原创

房产
亲子
时尚
数码
游戏

房产要闻

海棠湾!一所重量级国际学校真的来了!

亲子要闻

宝宝满眼都是妈妈,母子俩长得太像了,连笑容都一样

中年女性穿衣的重点,不在衣服的数量多少,而在于这三个方面

数码要闻

石头荣登《Google x Kantar BrandZ 中国全球化品牌 2024 》 50强

模拟经营游戏《疯狂手机大亨》上架Steam 收购苹果,从这里开始

无障碍浏览 进入关怀版