1. 簡介
Lombok 是一個極有用的庫,它能夠消除冗餘代碼。如果您尚未熟悉它,我強烈建議您查看之前的教程——《項目 Lombok 簡介》。
在本文中,我們將演示它與 Spring 的 基於構造函數的依賴注入 結合使用的實用性。
2. 基於構造函數的依賴注入
使用 constructor-based Dependency Injection,在 Spring 中實現依賴注入的一種有效方法。這種方法強制我們明確地通過構造函數傳遞組件的依賴項。
與 Field-Based Dependency Injection 相比,它還提供了許多優勢:
- 無需創建測試特定的配置組件——依賴項在構造函數中明確注入
- 一致的設計——所有必需的依賴項由構造函數定義強調並管理
- 簡單的單元測試——減少了 Spring Framework 的開銷
- 可以自由使用 final 關鍵字
然而,由於需要編寫構造函數,因此它通常會導致代碼庫顯著增大。 考慮 GreetingService 和 FarewellService 的兩個示例:
@Component
public class GreetingService {
@Autowired
private Translator translator;
public String produce() {
return translator.translate("hello");
}
}@Component
public class FarewellService {
private final Translator translator;
public FarewellService(Translator translator) {
this.translator = translator;
}
public String produce() {
return translator.translate("bye");
}
}基本上,這兩個組件的作用相同——它們都調用一個可配置的 翻譯器,傳遞一個特定任務的詞語。
然而,第二種變體則更加晦澀,這是由於構造函數的樣板代碼造成的,這些代碼並沒有為代碼帶來任何實際價值。
翻譯器 annotation.
3. 使用 Lombok 進行構造器注入
藉助 Lombok,您可以為任何類的所有字段生成構造器(使用 @AllArgsConstructor)或所有 final 類字段的構造器(使用 @RequiredArgsConstructor)。 此外,如果您仍然需要一個空構造器,則可以附加一個額外的 @NoArgsConstructor 註解。
讓我們創建一個第三個組件,類似於前兩個組件:
@Component
@RequiredArgsConstructor
public class ThankingService {
private final Translator translator;
public String produce() {
return translator.translate("thank you");
}
}上面的註解會導致 Lombok 生成一個構造函數:
@Component
public class ThankingService {
private final Translator translator;
public String thank() {
return translator.translate("thank you");
}
/* Generated by Lombok */
public ThankingService(Translator translator) {
this.translator = translator;
}
}4. 多個構造函數
構造函數不必進行標註,只要在一個組件中只有一個,Spring 就能明確地選擇它來實例化一個新的對象。一旦出現多個構造函數,您還需要標註用於由 IoC 容器使用的那個。
請考慮 ApologizeService 示例:
@Component
@RequiredArgsConstructor
public class ApologizeService {
private final Translator translator;
private final String message;
@Autowired
public ApologizeService(Translator translator) {
this(translator, "sorry");
}
public String produce() {
return translator.translate(message);
}
}上述組件可選擇性地配置 message 字段,該字段在組件創建後無法更改(因此沒有 setter 方法)。因此,我們需要提供兩個構造函數——一個具有完整配置,另一個具有隱式的默認 message 值。
除非一個構造函數被標記為使用 @Autowired、@Inject 或 @Resource,否則 Spring 將拋出錯誤。
Failed to instantiate [...]: No default constructor found;如果我們要對 Lombok 生成的構造函數進行註解,則需要通過將註解傳遞給 @AllArgsConstructor 的 onConstructor 參數來實現:
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ApologizeService {
// ...
}onConstructor 參數接受一個註解數組(或在特定示例中,單個註解),用於在生成的構造函數上添加。雙下劃線命名法是為了兼容性問題而引入的。根據文檔:
這種怪異的語法是為了使該功能在 javac 7 編譯器中工作,
@__類型是對註解類型__(雙下劃線) 的引用,實際上__類型並不存在;這使得 javac 7 延遲終止編譯過程,因為一個註解處理器可能會稍後創建__類型。
5. 總結
在本教程中,我們展示了在構造方法注入和基於字段的注入之間,無需偏愛基於字段的注入以減少樣板代碼的問題。
藉助Lombok,可以在不影響運行時性能的情況下自動化常見的代碼生成,將冗長、晦澀的代碼縮短為單行註解。