SpringBoot 構造器注入、Setter方法注入和Field注入對比

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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章