Guice 中的 @Provides 與 Provider 類
1. 簡介
在本教程中,我們將討論 Guice、Google 的依賴注入框架,以及@Provides
註解與 Guice 中的Provider
類別有何不同.
2. Guice 基礎知識
Google 為 Java 5 及更高版本創建了一個名為 Guice 的輕量級依賴注入 (DI) 框架。儘管它不像 Spring 那樣是一個全面的框架,但 Guice 功能豐富,旨在使依賴注入更簡單。
Guice 允許將依賴項傳遞或註入對象,遵循軟體設計中的 DI 模式。它使用 Java 程式碼和註解來定義連接對象,而不是其他一些 DI 框架使用的配置。
Guice 有以下關鍵組件:
- Guice 大量使用標記注入點的註釋
- Guice 有模組的概念,它定義
bindings
- 將介面與實際實作連接起來的綁定
- 負責創建和連接物件的注入器
Guice 與 Spring 類似,支援建構函式註入、方法注入以及欄位或參數注入。我們用Inject
註解來註解建構函式、模組或欄位。
綁定定義了 Guice 如何將依賴項注入到類別中。我們在模組類別中定義這些綁定,它們是AbstractModule
的實作.
為了在我們的 Maven 專案中使用 Google Guice,我們需要在pom.xml
中新增以下相依性:
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>7.0.0</version>
</dependency>
3. Guice 中的提供程序類
提供者類別用於管理物件建立過程。當實例化過程很複雜或需要額外的邏輯時,它們很有用,因為使用依賴注入來管理它們很有挑戰性。
提供者類別實作com.google.inject.Provider<T>
接口,該接口定義了單一T get()
方法。此方法的實作傳回所需類型的實例。
讓我們了解如何定義和使用Provider
類別。
3.1. Provider
接口
對於我們的例子,讓我們考慮一個通知客戶端事件的Notifier
介面。 Notifier
程序實作可以是電子郵件或電話類型:
public interface Notifier {
void sendNotification(String message);
}
我們為這個範例定義了EmailNotifier
類,並使其成為Notifier
類型的Provider<T>
類別:
public class EmailNotifier implements Notifier, Provider<Notifier> {
private String smtpUrl;
private String user;
private String password;
private EmailNotifier emailNotifier;
@Override
public Notifier get() {
// perform some initialization for email notifier
this.smtpUrl = "smtp://localhost:25";
emailNotifier = new EmailNotifier();
return emailNotifier;
}
@Override
public void sendNotification(String message) {
log.info("Sending email notification: " + message);
}
}
值得注意的是,我們實作com.google.inject.Provider
來表示我們的Provider
實現,並且我們在get()
方法中初始化提供者。
3.2.將Provider
者綁定到類型
現在已經定義了Provider
,我們需要在模組類別中綁定這種關聯。我們透過擴展AbstractModule
並重寫configure()
方法來建立模組類別:
public class MyGuiceModule extends AbstractModule {
@Override
protected void configure() {
bind(Notifier.class).to(EmailNotifier.class);
}
}
這會通知 Guice 始終呼叫EmailNotifier
的get()
方法來注入Notifier
的依賴項。
讓我們驗證依賴關係:
@Test
public void givenGuiceProvider_whenInjecting_thenShouldReturnEmailNotifier() {
// Create a Guice injector with the NotifierModule
Injector injector = Guice.createInjector(new NotifierModule());
// Get an instance of Notifier from the injector
Notifier notifier = injector.getInstance(Notifier.class);
// Assert that notifier is of type EmailNotifier
assert notifier != null;
assert notifier instanceof EmailNotifier;
}
雖然這是注入EmailNotifier
依賴項的簡潔方法,但應該注意,這不是最好的方法,特別是當圖中有多個Notifier
實作時。
讓我們考慮這個範例中的第二種Notifier
類型, PhoneNotifier
。 Guice 不允許以這種方式將兩個類別綁定到Notifier
。
解決方案是使用Named
註解。 Guice 中的Named
註解是一個內建綁定註解,用於透過將每個綁定與唯一的字串識別碼關聯起來,來區分相同類型的多個綁定。
當我們需要注入PhoneNotifier
和EmailNotifier
依賴項時,我們會執行以下操作:
@Override
protected void configure() {
bind(Notifier.class).annotatedWith(Names.named("Email"))
.toProvider(EmailNotifier.class);
bind(Notifier.class).annotatedWith(Names.named("Phone"))
.toProvider(PhoneNotifier.class);
}
這消除了 Guice 可能面臨的任何歧義。在我們的服務實作中,我們使用實作的名稱來指定我們希望 Guice 選擇哪個實作:
public class MyService {
private final Notifier emailNotifier;
private final Notifier phoneNotifier;
@Inject
public MyService(@Named("Email") Notifier emailNotifier, @Named("Phone") Notifier phoneNotifier) {
this.emailNotifier = emailNotifier;
this.phoneNotifier = phoneNotifier;
}
}
4. @在Guice中Provides
註解
Provides
註解嘗試定義一個比Provider
類別更簡單的替代方案。 Guice
中的@Provides
註解旨在註解可以充當綁定提供者的方法。
該註釋還提供了將附加相依性聲明為方法參數的能力。
4.1. @Provides
的實現
Provides
註解始終在模組類別中定義,並且必須使用方法進行註解。此方法的傳回類型成為依賴注入系統中的綁定類型。
讓我們透過一個名為Logger
的新介面來理解這一點:
public interface Logger {
void log(String message);
}
有了這個註釋,就不需要新的Provider
類別了。我們在模組類別中加入一個方法:
@Provides
public Logger provideLogger() {
return new Logger() {
@Override
public void log(String message) {
log.info("Logging message: " + message);
}
};
}
這裡有兩件重要的事情要注意。首先,我們可以定義一個實作Logger
介面的類,並在該方法中建立該類別的新實例。但是,我們只需建立一個內部匿名類別作為一種更短的替代方案。
其次,不需要在模組類別的configure()
方法中明確宣告綁定。
讓我們在這裡驗證依賴關係:
@Test
public void givenGuiceProvider_whenInjectingWithProvides_thenShouldReturnCustomLogger() {
Injector injector = Guice.createInjector(new MyGuiceModule());
Logger logger = injector.getInstance(Logger.class);
assert logger != null;
Assertions.assertNotNull(logger.log("Hello world"));
}
5. @Provides
和Provider
的差別
在本節中,我們來總結一下差異:
@Provides |
Provider 程式類 |
---|---|
提供簡單性,非常適合包含一次性綁定的場景 | 需要更多樣板代碼 |
無需明確綁定;注入器自動使用@Provides 方法 |
需要在模組中明確綁定 |
可以是靜態的(更有效率)或實例(可以存取模組欄位) | 始終基於實例 |
最適合簡單的實例邏輯 | 最適合複雜的實例邏輯 |
不太適合根據運行時條件建立不同的實例 | 非常適合根據運行時條件或第三方整合建立實例 |
並非天生懶惰 | 支援延遲實例化 |
5. 結論
在本文中,我們探討了 Guice 中的Provider
類別及其替代方案Provides
註解。這些方法有助於 Guice 中的依賴注入。我們也討論了這兩種方法的優點和缺點。
與往常一樣,程式碼可在 GitHub 上取得。