SpringBoot 构造器注入、Setter方法注入和Field注入对比

摘要:
经过查阅资料看到,Spring推荐使用构造器注入的方式,下面介绍构造器注入到底有什么玄机。在对比Setter方法注入和构造器注入的时候分别引用的Spring官方文档的第二段阐述了除推荐方式的另一种方式的特点。Setter注入应该被用于可选依赖项。构造器注入有利于强制依赖。
0. 引入

今天在看项目代码的时候发现在依赖注入的时候使用了构造器注入,之前使用过 Field 注入和 Setter 方法注入,对构造器注入不是很了解。经过查阅资料看到,Spring 推荐使用构造器注入的方式,下面介绍构造器注入到底有什么玄机。

1. 常见的三种注解注入方式对比

Field 注入

@Controller
public class HelloController {
    @Autowired
    private AlphaService alphaService;
    @Autowired
    private BetaService betaService;
}

field 注入方式是使用最多的,原因是这种方式使用起来非常简单,代码更加简洁。

Setter 方法注入

@Controller
public class HelloController {
    private AlphaService alphaService;
    private BetaService betaService;
    
    @Autowired
    public void setAlphaService(AlphaService alphaService) {
        this.alphaService = alphaService;
    }
    @Autowired
    public void setBetaService(BetaService betaService) {
        this.betaService = betaService;
    }
}

在 Spring 3.x 刚推出的时候,Spring 官方在对比构造器注入和 Setter 注入时,推荐使用 Setter 方法注入:

Spring 3.x Constructor-based or setter-based DI?

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 方式注入可以让类在之后重新配置和重新注入;

Constructor 注入

@Controller
public class HelloController {
    private final AlphaService alphaService;
    private final BetaService betaService;
    
    @Autowired
    public HelloController(AlphaService alphaService, BetaService betaService) {
        this.alphaService = alphaService;
        this.betaService = betaService;
    }
}

Spring 4.x 的时候,Spring 官方在对比构造器注入和 Setter 注入时,推荐使用构造器注入方式:

Spring 4.x Constructor-based or setter-based DI?

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.

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. Management through JMX MBeans is therefore a compelling use case for setter injection.

因为使用构造器注入方式注入的组件不可变,且保证了需要的依赖不为 null。此外,构造器注入的组件总是能够在完全初始化的状态返回给客户端(调用方);对于很多参数的构造器说明可能包含了太多了职责,违背了单一职责原则,表示代码应该重构来分离职责到合适的地方。

2. 构造器注入还是 Setter 注入?

在对比 Setter 方法注入和 构造器注入的时候 分别引用的 Spring 官方文档的第二段阐述了除推荐方式的另一种方式的特点。

在 Spring 3.x 的时候 Spring 推荐 Setter 方法注入,第二段表示:一些纯粹主义者喜欢基于构造函数的注入。提供所有对象依赖项意味着对象总是在完全初始化状态下返回给客户机(调用)代码。缺点是对象不太容易重新配置和重新注入。

在 Spring 4.x 的时候 Spring 推荐构造器注入,第二段表示:Setter 注入应该主要用于可选的依赖项,这些依赖项可以在类中分配合理的默认值。否则,必须在代码使用依赖项的任何地方执行非空检查。setter 注入的一个好处是,setter 方法使该类的对象能够在以后重新配置或重新注入。

Setter 注入应该被用于可选依赖项。当没有提供它们时,类应该能够正常工作。在对象被实例化之后,依赖项可以在任何时候被更改。

构造器注入有利于强制依赖。通过在构造函数中提供依赖,您可以确保依赖对象在被构造时已准备好被使用。在构造函数中赋值的字段也可以是final的,这使得对象是完全不可变的,或者至少可以保护其必需的字段。

构造器注入还可以避免 Field 注入的循环依赖问题,比如 在 Alpha 中注入 Beta,又在 Beta 中注入 Alpha。如果使用构造器注入,在 Spring 启动的时候就会抛出 BeanCurrentlyInCreationException 提醒循环依赖。

参考:

https://docs.spring.io/spring-framework/docs/3.1.x/spring-framework-reference/html/beans.html#d0e2778

https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/beans.html#beans-constructor-injection

https://www.vojtechruzicka.com/field-dependency-injection-considered-harmful/

免责声明:文章转载自《SpringBoot 构造器注入、Setter方法注入和Field注入对比》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇ansible debug模块学习笔记java 反射 动态代理下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

《Java编程思想》阅读笔记6

“Java的基本理念是:结构不佳的代码不能运行。”,从我原来使用Java的异常机制的感觉是不太喜欢的,因为他将整个异常的调用轨迹都放出来了,给人一种恐怖的感觉,现在慢慢体会着这里面的种种好处,感受着他们的设计思路。1.开发异常的初衷    在Java中,异常机制的使用的初衷是为了方便程序员处理可能的错误,其中一个重要原则是“只有在你知道如何处理的情况下才捕获...

2020前端面试题常问集锦

以下为常备面试题集锦,面好多家公司大都问的如此(后续更新补);还有一些算法和手写代码后面整理; js陈述类型1、Es6的class和构造函数的区别: class xx { }(1)不存在变量提升(2)方法默认是不可枚举的,class所有方法没有原型对象prototype也没有构造器不能用new来调用; 2、普通函数和箭头函数的区别?(1)this指向不...

通过实例学习C#开发中的泛型

C#中所谓泛型:即通过参数化类型来实现在同一份代码上操作多种数据类型。泛型编程是一种编程范式,它利用“参数化类型”将类型抽象化,从而实现更为灵活的复用。   C#泛型赋予了代码更强的类型安全,更好的复用,更高的效率,更清晰的约束。   C#泛型机制简介   C#泛型能力由CLR在运行时支持,区别于C++的编译时模板机制,和java的编译时的“搽拭法”。这...

Scala学习(八)---Scala继承

Scala继承 摘要: 在本篇中,你将了解到Scala的继承与Java和C++最显著的不同。要点包括: 1. extends、final关键字和Java中相同 2. 重写方法时必须用override 3. 只有主构造器可以调用超类的主构造器 4. 你可以重写字段 在本篇中,我们只探讨类继承自另一个类的情况。继承特质的内容后面会详细介绍 扩展类...

Effective java -- 1

写博客我也不知道是不是一个好习惯,但是目前还不知道有什么其他更有效率的学习方法。现在的学习方法:看书,写博客。如果看明白一个东西,去写博客的话,这通常是一个浪费时间的行为,但是这个过程同样帮助自己二次记忆。并不知道写博客到底好不好。就先按照这个来吧。开始新的一本书,《Effective Java》 第一条:考虑用静态工厂方法代替构造器优点 静态工厂有名称...

《JavaScript面向对象编程指南》

第一章、引言 1.5 面向对象的程序设计常用概念 对象(名词):是指“事物”在程序设计语言中的表现形式。 这里的事物可以是任何东西,我们可以看到它们具有某些明确特征,能执行某些动作。 这些对象特征就叫做属性(形容词),动作称之为方法(动词)。 类:实际上就是对象的设计蓝图或制作配方。类更多的是一种模板,而对象就是在这些模版的基础上被创建出来的。 封装:主要...