前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring字段注入存在哪些问题,你知道吗?

Spring字段注入存在哪些问题,你知道吗?

作者头像
阿凯
发布2021-08-19 14:22:25
1.2K0
发布2021-08-19 14:22:25
举报
文章被收录于专栏:程序员阿凯

阅读文本大概需要 7 分钟。

Spring字段注入存在哪些问题,你知道吗?

昨天有个同学面试回来向我求助,说面试官问他Spring字段注入存在什么问题,他当时没有回答上来。

是的,Spring的字段注入,是我们最常使用的注入方式,但是却一直存在着隐藏着的问题。

这个问题其实在使用IDEA的时候已经提示我们了,只是大部分时候我们视而不见,或者明明知道,却选择了忽视。

现在我们来仔细看看吧,这是我们经常使用的一个场景,在一个Controller里注入需要使用的Service。

一般我们都是在这个字段上打上Autowired注解,这样就实现了字段注入。

代码很简单,但是你是否注意到 IDEA 给我们在 @Autowired注解下那提示的波浪线呢?

我们把鼠标移上去看一下:

Field injection is not recommended

英文稍微好一点的同学已经知道是什么意思了,这是Spring官方不建议我们这样用啊。

英文稍微没有那么好的也没有关系,我们利用翻译工具看一下:

是的,Spring官方不建议我们使用字段注入的方式,并且建议我们换一种方式。

哈哈,推荐使用构造方法注入。

那么疑问来了,这是为什么呢?

这就要回到咱们最开始的那道面试题了,Spring字段注入存在哪些问题呢?

是的,通过 @Autowired 注解配合字段注入的实现方式,非常简单而直接,代码的可读性也很强。

而且,事实上在我们的开发过程中,字段注入是三种注入方式中最常用、也是最容易使用的一种。

但它也是三种注入方式中最应该避免使用的。

原因有三点:

  1. 注入的对象外部不可见

字段注入的最大问题是就是对象的外部可见性问题。

在前面的 CourseController 类中,我们通过定义一个私有变量 ICourseService 来注入该接口的实例。显然,这个实例只能在 CourseController 类中被访问,脱离了容器环境我们无法访问这个实例。

既,如果我们不是通过Spring容器注入 CourseController 的实例,而是自己直接new对象的方式创建。

那么,结果就是一个 NullPointerException。

原因就在于,无法在 CourseController 的外部实例化 ICourseService 对象。

采用字段注入,类与容器的耦合度过高,我们无法脱离容器来使用目标对象。

这种做法实际上是不符合 JavaBean 开发规范的,而且可能一直无法发现空指针异常的存在。

  1. 循环依赖

字段注入的第二个问题,是可能导致潜在的循环依赖。

我们来看下面这段代码:

这里的 ClassA 和 ClassB 实际上就发生了循环依赖。

上述代码在 Spring 中是合法的,容器启动时并不会报任何错误,只有在使用到具体某个 ClassA 或 ClassB 时才会报错。

而这个时候,往往为时已晚。

  1. 无法保证注入对象不可变

使用字段注入的方式,我们无法设置需要注入的对象为 final,也无法注入那些不可变对象。

这是因为字段注入的对象必须在类实例化后在进行实例,而final修饰的对象必须提前到对象声明的时候或者在构造方法中实例化。

基于以上三点,无论是 IDEA,还是 Spring 官方,都不推荐我们开发人员使用字段注入这种注入模式,而是推荐构造器注入。

在面试中,针对字段注入,请记住它主要的三点缺陷:

不具备外部可见性、会导致循环依赖,以及无法注入不可变对象。

那么,我们有什么办法解决这些问题呢?

这就要回到另一道经典的面试题上来了,如何在 Spring 中注入对象呢?Spring 的依赖注入有哪些呢?

这道题对于我们的同学来说还是比较简单的。

Spring 为开发人员提供了三种不同的依赖注入类型,分别是字段注入构造器注入Setter 方法注入

既然字段注入有问题,那么我们来看一看,其他两种注入方式呢。

构造方法注入

关于构造器注入,面试中往往会以这样的形式考察你:

构造器是 Spring 官方推荐的依赖注入类型,你知道它有哪些特性吗?

或者换种问法,构造器注入相比字段注入的优势在哪里?

构造器注入的形式也很简单,就是通过类的构造函数来完成对象的注入,示例代码如下所示:

可以看到构造器注入能解决对象外部可见性的问题,因为这里的 ICourseService 是通过 CourseController 构造函数进行注入的,所以势必可以脱离 CourseController 而独立存在。

关于构造器注入,我们也建议你引用 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 关键词来修饰所依赖的对象,而依赖不为空是指所传入的依赖对象肯定是一个实例对象,避免出现空指针异常。

同时,基于构造器注入,如果存在前面介绍的 ClassA 和 ClassB 之间的循环依赖关系,我们会这样注入对象:

那么,在 Spring 容器启动的时候,就会抛出一个循环依赖异常,从而提醒你避免循环依赖。

代码语言:javascript
复制
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'CLassA' defined in file[]: 
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'CLassB' defined in file []: 
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'CLassA': 
Requested bean is currently in creation: 
Is there an unresolvable circular reference?

如此看来,字段注入的三大问题,就都可以通过使用构造器注入的方式来解决。

但是,构造器注入就没有问题了吗?显然不是!

当构造函数中存在较多依赖对象的时候,大量的构造器参数会让代码显得比较冗长。

假设一个类的构造器需要多个参数,那么我们想要使用这个类时,就需要事先准备好这些参数,并严格按照构造器指定的顺序一一进行传入。

那么,无论从代码的可读性还是可维护角度而言,这都不是很符合最佳实践。

这时候就可以引入 Setter 方法注入。

Setter 方法注入

Setter 方法注入和构造器注入看上去有点类似,而且它比构造函数更具可读性。

我们可以把多个依赖对象分别通过 Setter 方法逐一进行注入。

另一方面,Setter 方法可以很好解决应用程序中的循环依赖问题,如下所示,通过 Setter 方法注入的ClassA 和 ClassB 代码是可以正确执行的:

而且,通过 Setter 注入,还可以对依赖对象进行多次重复注入,这在构造器注入中是无法实现的。

最后,概括起来就是:

*构造器注入适用于强制对象注入,注入的对象是不可变的

*Setter 注入适合于可选对象注入,可以解决循环依赖问题

*字段注入应该避免,对象无法脱离 Spring容器而独立运行,。

好了,现在你能回答出三种依赖注入类型的相关内容了,依赖注入用得好,Spring 框架面试轻松搞定。

优秀啊,骚年!后期更多优选推文,各种资料、分享猛料放出,关注公众号,获取实时动态:

大家还有什么需求,也可以后台留言给我,公众号上还有其他学习资源哦....

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员阿凯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Spring字段注入存在哪些问题,你知道吗?
    • Field injection is not recommended
      • 原因有三点:
      • 那么,我们有什么办法解决这些问题呢?
        • 构造方法注入
          • Setter 方法注入
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档