使用 Jerseys HK2 进行基本依赖注入
泽西岛(2)使用 HK2 作为其依赖注入(DI)系统。我们可以使用其他注入系统,但其基础设施使用 HK2 构建,并允许我们在我们的应用程序中使用它。
使用 Jersey 设置简单依赖注入只需几行代码。比方说,我们有一个服务,我们想要注入我们的资源。
public class GreetingService {
public String getGreeting(String name) {
return "Hello " + name + "!";
}
}
我们希望将此服务注入 Jersey 资源
@Path("greeting")
public class GreetingResource {
@Inject
public GreetingService greetingService;
@GET
public String get(@QueryParam("name") String name) {
return this.greetingService.getGreeting(name);
}
}
为了使注入工作,我们所需要的只是一个简单的配置
@ApplicationPath("/api")
public class AppConfig extends ResourceConfig {
public AppConfig() {
register(GreetingResource.class);
register(new AbstractBinder() {
@Override
protected void configure() {
bindAsContract(GreetingService.class);
}
});
}
}
在这里我们说我们想要将 GreetingService
绑定到注入系统,并将它作为可注入的相同类通告。最后一个陈述意味着我们只能将它作为 GreetingService
注入,并且(可能很明显)不能被任何其他类注入。正如你稍后将看到的,可以更改此设置。
而已。这就是你所需要的。如果你不熟悉此 ResourceConfig
配置(可能你使用的是 web.xml),请参阅 SO Docs 上的在 Jersey 中配置 JAX-RS 主题。
注意: 上面的注入是字段注入,其中服务被注入资源的字段。另一种类型的注入是构造函数注入,其中服务被注入到构造函数中
private final GreetingService greetingService;
@Inject
public GreetingResource(GreetingService greetingService) {
this.greetingService = greetingService;
}
这可能是首选的方式,而不是现场注入,因为它使资源更容易进行单元测试。构造函数注入不需要任何不同的配置。
好了,现在我们可以说,GreetingService
不是一个类,而是一个接口,我们有一个它的实现(这很常见)。要配置它,我们将在上面的 configure
方法中使用以下语法
@Override
protected void configure() {
bind(NiceGreetingService.class).to(GreetingService.class);
}
这读作“bind NiceGreetingService
,并将其宣传为 GreetingService
”。这意味着我们可以在上面的 GreetingResource
中使用完全相同的代码,因为我们将合同宣传为 GreetingService
而不是 NiceGreetingService
。但注入时的实际实施将是 NiceGreetingService
。
那么范围呢?如果你曾经使用过任何注入框架,那么你将会遇到范围的概念,它决定了服务的生命周期。你可能听说过请求范围,其中服务仅在请求的生命周期内存在。或者是 Singleton Scope
,其中只有一个服务实例。我们也可以使用以下语法配置这些范围。
@Override
protected void configure() {
bind(NiceGreetingService.class)
.to(GreetingService.class)
.in(RequestScoped.class);
}
默认范围是 PerLookup
,这意味着每次请求此服务时,都会创建一个新服务。在上面的示例中,使用 RequestScoped
,将为单个请求创建新服务。这可能与 PerLookup
相同或不同,具体取决于我们尝试注入的数量。我们可能会尝试将其注入过滤器和资源。如果这是 PerLookup
,那么将为每个请求创建两个实例。在这种情况下,我们只想要一个。
另外两个可用的范围是 Singleton
(仅创建一个实例)和 Immediate
(如 Singleton
),但是在启动时创建(而使用 Singleton
,它不会在第一个请求之前创建)。
除了绑定类之外,我们还可以使用实例。这会给我们一个默认的单例,所以我们不需要使用 in
语法。
@Override
protected void configure() {
bind(new NiceGreetingService())
.to(GreetingService.class);
}
如果我们有一些复杂的创建逻辑或需要服务的某些请求上下文信息,该怎么办?在这种情况下有 Factory
s。我们可以注入我们的泽西资源的大多数东西,我们也可以注入一个 Factory
。举个例子
public class GreetingServiceFactory implements Factory<GreetingService> {
@Context
UriInfo uriInfo;
@Override
public GreetingService provide() {
return new GreetingService(
uriInfo.getQueryParameters().getFirst("name"));
}
@Override
public void dispose(GreetingService service) {
/* noop */
}
}
这里我们有一个工厂,它从 UriInfo
获取请求信息,在这种情况下是一个查询参数,我们从中创建 GreetingService
。要配置它,我们使用以下语法
@Override
protected void configure() {
bindFactory(GreetingServiceFactory.class)
.to(GreetingService.class)
.in(RequestScoped.class);
}
而已。这些只是基础知识。香港和泽西岛要做的事情要多得多。