使用 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);
}
而已。這些只是基礎知識。香港和澤西島要做的事情要多得多。