簡體   English   中英

使用guice進行客戶端sdk /庫設計模式的依賴注入

[英]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);
}

其中HttpRequestHttpResponse也是自定義類/接口。 但是向最終用戶提供這種擴展點很少是合適的,特別是如果你沒有一些參考實現(這意味着用戶必須為他/她想要的依賴創建這個外觀)。 在極少數情況下這合適的,但可能這不是你的情況。

對於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真的很值得。

  1. 您可以執行模塊覆蓋,不建議用於生產。 你可以在這里找到更多在Guice中覆蓋綁定
    1. 您可以使用@ImplementedBy創建綁定,但顯式綁定接口將覆蓋該注釋綁定。 因此,如果可能的話,您將使用@ImplementeBy創建框架,並且第3方開發人員將在其模塊中顯式綁定來覆蓋它。 了解更多https://code.google.com/p/google-guice/wiki/JustInTimeBindings
    2. 無論如何,我不熟悉這些方法。 我建議創建一個抽象的ApiClient並讓第3方開發人員實現開放點。 也許你應該介紹一些像@Client這樣必須實現ClientApi annotations 然后,您的模塊將使用@Client批注搜索類路徑以實現ClientApi 讓我們說注釋將包含一個值@Client('apache-http') ,你將引入一個配置屬性'api-client',如果你想使用不同的東西,它將被設置為default的默認實現或apache-http 那么,你應該考慮它,因為你的ApiClient的完整性可以用錯誤的綁定輕易破壞:)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM