热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:关于Spring注入方式的几道面试题,你能答上么?

篇首语:本文由编程笔记#小编为大家整理,主要介绍了关于Spring注入方式的几道面试题,你能答上么?相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了关于Spring注入方式的几道面试题,你能答上么?相关的知识,希望对你有一定的参考价值。









作者:Richard_Yi


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

装配顺序:


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


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


  • 如果有@Qualifier注解,则按照@Qualifier指定的name进行匹配,查找name为svcA的bean

  • 如果没有,则按照变量名进行匹配,查找name为svcA的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方式进行装配;如果没有匹配,按照byTypeh进行装配。


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;
@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 的依赖注入有更深的理解。最后,关注Java知音公众号,回复“后端面试”,送你一份面试题宝典!


推荐好文


>>【练手项目】基于SpringBoot的ERP系统,自带进销存+财务+生产功能>>分享一套基于SpringBoot和Vue的企业级中后台开源项目,代码很规范!
>>能挣钱的,开源 SpringBoot 商城系统,功能超全,超漂亮!






推荐阅读
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
author-avatar
欧罗巴法域_430
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有