我正在为Web API构建客户端SDK,并尝试通过guice应用依赖项注入(inject)。第三方将使用此Java客户端作为访问我们的API的方式。
我希望能够注入(inject)我的外部依赖项(使用了HTTP客户端等),并为开发人员提供了一种方式,以便在他们想要或我想自己更改实现的情况下注入(inject)那些依赖关系的不同版本(一个很好的依赖关系案例)注入(inject)吧?)。
但是,为了连接依赖关系,我必须让我的库的用户创建一个注入(inject)器等,如下所示:
Injector injector = Guice.createInjector(new MyAPIClientModule(url, username, password));
this.service = injector.getInstance(MyAPIService.class);
我不想将其推给我的库用户,但我仍然想让用户能够选择其他实现或基础HTTP库等。
我是否在某种程度上缺少guice或DI的要点?使用guice时,这是标准做法吗?
还是应该将其包装在另一个进行注入(inject)的类中,并向第三方用户仅提供一个示例Java对象?
最佳答案
对于DI来说,这是一个很好的案例。诸如HTTP客户端之类的外部依赖关系通常具有具体的接口(interface),除了确切的依赖关系之外,没有人实现。就个人而言,鉴于交换基础HTTP客户端不会影响其体系结构,因此我无法想象您的程序是如何编写的,也就是说,除非您为其提供自己的外观,否则类似
public interface HttpClient {
HttpResponse send(HttpRequest request);
}
其中
HttpRequest
和HttpResponse
也是自定义类/接口(interface)。但是,向最终用户提供这种扩展点几乎是不合适的,尤其是在您没有引用实现的情况下(这意味着用户将不得不为他/她想要的依赖关系创建此外观)。在极少数情况下是合适的,但是这不是您的情况。对于DI,通常也不会具有相同依赖项的不同版本,因为交换版本可以在构建/组装时完成。
如果您想让用户能够提供自己的某些“运动部件”库的实现,那么首先必须为所有这些运动部件定义严格的接口(interface)。换句话说,提供一组
interface
,您的用户必须扩展这些Module
,并将其注入(inject)您的类中。然后,创建由Guice模块组成的“绑定(bind)空间”,并在这些模块中声明以下接口(interface)的要求:
public class SomeModule extends AbstractModule {
@Override
protected void configure() {
requireBinding(SomeUserAPI.class);
// Other bindings which probably use SomeUserAPI in implementations
}
}
通过声明必需的绑定(bind),可以确保除非有人提供给定类的某些实现,否则没有人可以在您的模块中混用。当然,如果无法找到绑定(bind),Guice仍然会失败,但是当您明确要求它时,您将获得更具体的错误消息以及模块的清晰接口(interface)。
然后,您为库创建特殊的“入口点”,唯一的责任是创建注入(inject)器并向用户提供类的实例。此类接受用户的Guice模块并将其集成到注入(inject)器中。
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(因为用户必须实现ojit_code接口(interface))。我认为除非您做一些奇怪的事情,否则您无法完全避免这种情况
Library.create(SomeUserAPIImpl.class, SomeUserAPI2Impl.class, ...)
也就是说,接受代表扩展点实现的类对象(然后将它们绑定(bind)到某些内部模块中)。但是我不认为从库界面中删除Guice确实值得。
关于java - 使用guice进行客户sdk/库设计模式的依赖注入(inject),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/22184736/