[英]Dependency injection using guice for a client sdk/library design pattern
我正在為Web API構建客戶端SDK並嘗試通過guice應用依賴注入。 第三方將使用此Java客戶端作為訪問API的方式。
我希望能夠注入我的外部依賴項(使用的HTTP客戶端等),並為開發人員提供一種方法,如果他們想要或者我自己想要更改實現,則可以注入不同版本的這些依賴項(依賴項的一個很好的例子)注射對嗎?)。
但是為了連接依賴項,我必須讓我的庫的用戶創建一個注入器等,如下所示:
Injector injector = Guice.createInjector(new MyAPIClientModule(url, username, password));
this.service = injector.getInstance(MyAPIService.class);
我不想把它推到我的庫的用戶,但我仍然希望讓用戶能夠選擇不同的實現或底層HTTP庫等。
我在某種程度上錯過了guice或DI的觀點嗎? 這是使用guice時的標准做法嗎?
或者我應該將其包裝在另一個執行注入的類中,並向第三方用戶提供一個示例Java對象?
我希望能夠注入我的外部依賴項(使用的http客戶端等),並為開發人員提供一種方法,如果他們想要或者如果我想自己更改實現,則可以注入這些依賴項的不同版本(依賴項的一個好例子)注射對嗎?)。
對於DI而言,這是一個很好的理由。 像HTTP客戶端這樣的外部依賴關系通常具有具體的接口,除了完全依賴之外,沒有人實現。 我個人無法想象你的程序是如何編寫的,因為交換底層HTTP客戶端不會影響它的架構,也就是說, 除非你為它提供自己的外觀 ,比如
public interface HttpClient {
HttpResponse send(HttpRequest request);
}
其中HttpRequest
和HttpResponse
也是自定義類/接口。 但是向最終用戶提供這種擴展點很少是合適的,特別是如果你沒有一些參考實現(這意味着用戶必須為他/她想要的依賴創建這個外觀)。 在極少數情況下這是合適的,但可能這不是你的情況。
對於DI,通常也不是相同依賴的不同版本,因為交換版本可以在構建/組裝時完成。
如果您希望為用戶提供一些能力來提供他們自己的庫“移動部件”的實現,那么首先必須為所有這些移動部件定義嚴格的接口 。 換句話說,提供一組用戶必須擴展的interface
,以及在類中注入的interface
。
然后創建由Guice模塊組成的“綁定空間”,在這些模塊中,您可以聲明這些接口的要求:
public class SomeModule extends AbstractModule {
@Override
protected void configure() {
requireBinding(SomeUserAPI.class);
// Other bindings which probably use SomeUserAPI in implementations
}
}
通過聲明所需的綁定,您確保沒有人能夠在您的模塊中混合,除非它們提供給定類的某些實現。 當然,如果Guice無法找到綁定,Guice將會失敗,但是當您明確要求它時,您將獲得更具體的錯誤消息,以及模塊的清晰界面。
然后,您為庫創建特殊的“入口點”,其唯一的責任是創建注入器並為用戶提供類的實例。 該類接受用戶的Guice模塊並將其集成到注入器中。
public class Library {
private final Injector injector;
private Library(Module userModule) {
// SomeModule and AnotherModule are modules defined in the library
// and they are not the part of public interface of your library
this.injector = Guice.createInjector(userModule, new SomeModule(), new AnotherModule());
}
public static Library create(Module userModule) {
return new Library(userModule);
}
public MyAPIService myAPIService() {
return injector.getInstance(MyAPIService.class);
}
}
然后用戶像這樣使用它:
Library library = Library.create(new AbstractModule() {
@Override
protected void configure() {
// recall requireBinding(SomeUserAPI.class) statement we wrote previously,
// here we are "implementing" it
bind(SomeUserAPI.class).to(SomeUserAPIImpl.class);
// other bindings for your exposed interfaces
}
});
MyAPIService service = library.myAPIService();
在這種方法中,您允許用戶以整潔和受控的方式使用Guice DI擴展您的庫。
但是,您仍然需要向用戶公開Guice(因為用戶必須實現Module
接口)。 除非你做一些奇怪的事情,否則我認為你不能完全避免這種情況
Library.create(SomeUserAPIImpl.class, SomeUserAPI2Impl.class, ...)
也就是說,接受表示擴展點實現的類對象(然后在一些內部模塊中綁定它們)。 但我不認為從庫界面中消除Guice真的很值得。
@ImplementedBy
創建綁定,但顯式綁定接口將覆蓋該注釋綁定。 因此,如果可能的話,您將使用@ImplementeBy
創建框架,並且第3方開發人員將在其模塊中顯式綁定來覆蓋它。 了解更多https://code.google.com/p/google-guice/wiki/JustInTimeBindings ApiClient
並讓第3方開發人員實現開放點。 也許你應該介紹一些像@Client
這樣必須實現ClientApi
annotations
。 然后,您的模塊將使用@Client
批注搜索類路徑以實現ClientApi
。 讓我們說注釋將包含一個值@Client('apache-http')
,你將引入一個配置屬性'api-client',如果你想使用不同的東西,它將被設置為default
的默認實現或apache-http
。 那么,你應該考慮它,因為你的ApiClient的完整性可以用錯誤的綁定輕易破壞:)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.