Abstract類中使用@Autowire


背景

項目使用的就是SpringBoot默認的結構,我看了下,依賴注入使用了最不推薦的字段注入。

字段注入

為了保持項目風格統一,省的有些理論派挑刺,還是延續字段注入的操作。
某個業務場景下,有一個抽象的父類和多個具體的子類,子類中需要用到父類注入的對象。
當即有人就說,這么寫:

public abstract class AbstractClass{
    @Autowired
    protected InjectedBean injectedBean;
}

it works!
但是我們看到為了只讓子類使用該對象,我們使用了protected 訪問修飾符,但這意味着,子類也可以set該對象一個新的值。基礎知識。
哦,改進一下唄

public abstract class AbstractClass{
    @Autowired
    protected final InjectedBean injectedBean;
}

final加持,完美的解決了問題。很可惜,不可以。
為什么呢?

Having @Autowired and final on a field are contradictory.
The latter says: this variable has one and only one value, and it's initialized at construction time.
The former says: Spring will construct the object, leaving this field as null (its default value). Then Spring will use reflection to initialize this field with a bean of type WorkspaceRepository.
If you want final fields autowired, use constructor injection

ref: https://stackoverflow.com/questions/34580033/spring-io-autowired-the-blank-final-field-may-not-have-been-initialized

簡單來說,就是二者賦值的時機不統一造成了互斥。

構造函數注入

那我們使用上面提到的,通constructor injection注入試試

public abstract class AbstractClass{
    private InjectedBean injectedBean;

    @Autowired
    public void AbstractClass(InjectedBean injectedBean) {
        this.injectedBean = injectedBean;
    }
}

Spring 不會在抽象類的構造函數上解析 @Autowired 注解。可以通過子類的構造函數注入實現。

public abstract class AbstractClass{
    private InjectedBean injectedBean;

    public void AbstractClass(InjectedBean injectedBean) {
        this.injectedBean = injectedBean;
    }
}

public class ChildClass{
    private InjectedBean injectedBean;

    @Autowired
    public void ChildClass(InjectedBean injectedBean) {
        super(injectedBean);
    }
}

這么寫真的有些繁瑣了...而且,就我的項目而言,我的初衷是給子類使用,這么豈不是多此一舉。
好的,也就引出了Setter注入。

Setter注入

public abstract class AbstractClass{
    private LogRepository logRepository;
    @Autowired
    public final void setLogRepository(LogRepository logRepository) {
        this.logRepository = logRepository;
    }
}

Setter的時候,標記為public final
Getter的時候,標記為protected

可以說是最佳實踐了。

ref: https://segmentfault.com/a/1190000039053805

為什么不推薦使用字段注入?

事實上,當你使用IDE開發的時候,你使用了Spring的字段注入,你會得到一個提示:

Field injection is not recommended

當然是因為這種方式有好多缺點:

  1. 不允許不可變字段的聲明,像我們剛才說的 @Autowired protected final

  2. 代碼壞味道的潛在根源
    因為字段注入是如此的方便,你可以“只要需要”就注入一個你想操作的對象,結果不知不覺中注入了十幾個甚至幾十個(見賢思齊,我所在的項目中確實已經有這樣的問題了)。
    反之,如果我們通過構造函數注入,隨着注入對象的增多,構造函數的參數不斷變多,你明顯的就能聞到“壞味道”了,這時候你得開始想,是時候把當前類重新划分了,怎么能承擔如此多的職責呢?職責單一忘了嗎?SOLID天天盯着你們哪!

  3. 和Spring的容器緊耦合
    如果這點你不覺得有什么,那你是不是忘記寫單元測試了?
    單元測試的時候你也引入了Spring框架?就是為了使用“注入”這個功能?
    如果我們使用構造函數注入,或者Setter注入,我們在脫離Spring框架之后(比如單元測試)的時候,我們可以通過其他方式傳入目標對象。反之,我們只能強依賴Spring框架了。

  4. 隱藏了依賴
    當使用依賴項注入模式時,受影響的類應該通過公開構造函數中所需的依賴項或使用方法(setter)的可選依賴項,使用公共接口清楚地公開這些依賴項。當使用基於字段的依賴項注入時,類本質上是向外部世界隱藏這些依賴項。

ref: https://blog.marcnuri.com/field-injection-is-not-recommended


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM