jersey
Afhankelijkheid injectie met Jersey
Zoeken…
Basisafhankelijkheidsinjectie met HK2 van Jersey
Jersey (2) gebruikt HK2 als zijn afhankelijkheidsinjectiesysteem (DI). We kunnen andere injectiesystemen gebruiken, maar de infrastructuur is gebouwd met HK2 en stelt ons in staat om het ook binnen onze applicaties te gebruiken.
Het instellen van eenvoudige afhankelijkheidsinjectie met Jersey vergt slechts een paar regels code. Laten we zeggen dat we bijvoorbeeld een service hebben die we graag in onze bronnen willen injecteren.
public class GreetingService {
public String getGreeting(String name) {
return "Hello " + name + "!";
}
}
En we willen deze service in een Jersey-bron injecteren
@Path("greeting")
public class GreetingResource {
@Inject
public GreetingService greetingService;
@GET
public String get(@QueryParam("name") String name) {
return this.greetingService.getGreeting(name);
}
}
Om de injectie te laten werken, hebben we alleen een eenvoudige configuratie nodig
@ApplicationPath("/api")
public class AppConfig extends ResourceConfig {
public AppConfig() {
register(GreetingResource.class);
register(new AbstractBinder() {
@Override
protected void configure() {
bindAsContract(GreetingService.class);
}
});
}
}
Hier zeggen we dat we de GreetingService
aan het injectiesysteem willen binden en adverteren als injecteerbaar door dezelfde klasse. Wat de laatste verklaring betekent, is dat we het alleen als GreetingService
kunnen injecteren en (waarschijnlijk duidelijk) niet door een andere klasse. Zoals u later zult zien, is het mogelijk om dit te wijzigen.
Dat is het. Dat is alles wat je nodig hebt. Als u niet bekend bent met deze ResourceConfig
configuratie (misschien gebruikt u web.xml), raadpleegt u het onderwerp JAX-RS configureren in Jersey over SO Docs.
Opmerking: De bovenstaande injectie is een veldinjectie, waarbij de service in het veld van de resource wordt geïnjecteerd. Een ander type injectie is constructorinjectie, waarbij de service in de constructor wordt geïnjecteerd
private final GreetingService greetingService;
@Inject
public GreetingResource(GreetingService greetingService) {
this.greetingService = greetingService;
}
Dit is waarschijnlijk de beste manier om te gaan, in tegenstelling tot veldinjectie, omdat het de resource gemakkelijker maakt om te testen. Constructorinjectie vereist geen andere configuratie.
Ok laten we nu zeggen dat in plaats van een klasse, de GreetingService
een interface is en we hebben een implementatie ervan (wat heel gebruikelijk is). Om dat te configureren, zouden we de volgende syntaxis gebruiken in de bovenstaande configure
@Override
protected void configure() {
bind(NiceGreetingService.class).to(GreetingService.class);
}
Dit luidt als "bind NiceGreetingService
, en adverteer het als GreetingService
". Dit betekent dat we exact dezelfde code kunnen gebruiken in de GreetingResource
hierboven, omdat we het contract adverteren als GreetingService
en niet NiceGreetingService
. Maar de daadwerkelijke implementatie, wanneer geïnjecteerd, zal de NiceGreetingService
.
Hoe zit het nu met de scope? Als u ooit met een injectiekader hebt gewerkt, bent u het concept van scope tegengekomen, dat de levensduur van de service bepaalt. Je hebt misschien gehoord van een "Request Scope", waar de service alleen leeft voor de levensduur van het verzoek. Of een "Singleton Scope", waar er slechts één exemplaar van de service is. We kunnen deze scopes ook configureren met behulp van de volgende syntaxis.
@Override
protected void configure() {
bind(NiceGreetingService.class)
.to(GreetingService.class)
.in(RequestScoped.class);
}
Het standaardbereik is PerLookup
, wat betekent dat elke keer dat deze service wordt aangevraagd, een nieuwe wordt gemaakt. In het bovenstaande voorbeeld wordt met behulp van de RequestScoped
een nieuwe service gemaakt voor een enkele aanvraag. Dit kan al dan niet hetzelfde zijn als de PerLookup
, afhankelijk van het aantal plaatsen waar we het proberen te injecteren. We proberen het mogelijk in een filter en in een bron te injecteren. Als dit PerLookup
, zouden voor elke aanvraag twee instanties worden gemaakt. In dit geval willen we er maar één.
De andere twee beschikbare scopes zijn Singleton
(slechts één instantie gemaakt) en Immediate
(zoals Singleton
) maar wordt bij het opstarten gemaakt (terwijl het met Singleton
pas op het eerste verzoek wordt gemaakt).
Naast bindende klassen kunnen we ook gewoon een instantie gebruiken. Dit zou geeft ons een standaard singleton, dus we don noodzaak om het te gebruiken in
syntax.
@Override
protected void configure() {
bind(new NiceGreetingService())
.to(GreetingService.class);
}
Wat als we een complexe logica voor het maken hebben of wat contextinformatie voor de service nodig hebben. In dit geval zijn er Factory
s. De meeste dingen die we in onze Jersey-middelen kunnen injecteren, kunnen we ook in een Factory
injecteren. Neem bijvoorbeeld
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 */
}
}
Hier hebben we een fabriek, die aanvraaginformatie van UriInfo
, in dit geval queryparameters, en we maken de GreetingService
basis daarvan. Om het te configureren, gebruiken we de volgende syntaxis
@Override
protected void configure() {
bindFactory(GreetingServiceFactory.class)
.to(GreetingService.class)
.in(RequestScoped.class);
}
Dat is het. Dit zijn slechts de basisbegrippen. Er zijn nog veel meer dingen die HK en Jersey te doen hebben.