Partant de zéro sans aucune connaissance préalable de Jersey 1.x, j'ai du mal à comprendre comment configurer l’injection de dépendance dans mon projet Jersey 2.0.
Je comprends également que HK2 est disponible dans Jersey 2.0, mais je ne trouve pas les docs utiles pour l’intégration de Jersey 2.0.
@ManagedBean
@Path("myresource")
public class MyResource {
@Inject
MyService myService;
/**
* Method handling HTTP GET requests. The returned object will be sent
* to the client as "text/plain" media type.
*
* @return String that will be returned as a text/plain response.
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/getit")
public String getIt() {
return "Got it {" + myService + "}";
}
}
@Resource
@ManagedBean
public class MyService {
void serviceCall() {
System.out.print("Service calls");
}
}
pom.xml
<properties>
<jersey.version>2.0-rc1</jersey.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-common</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jax-rs-ri</artifactId>
</dependency>
</dependencies>
Je peux faire en sorte que le conteneur démarre et serve ma ressource, mais dès que j'ajoute @Inject to MyService, le framework lève une exception:
SEVERE: Servlet.service() for servlet [com.noip.MyApplication] in context with path [/jaxrs] threw exception [A MultiException has 3 exceptions. They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=MyService,parent=MyResource,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,1039471128)
2. Java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.noip.MyResource errors were found
3. Java.lang.IllegalStateException: Unable to perform operation: resolve on com.noip.MyResource
] with root cause
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=MyService,parent=MyResource,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,1039471128)
at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.Java:74)
Mon projet de démarrage est disponible sur GitHub: https://github.com/donaldjarmstrong/jaxrs
Vous devez définir une AbstractBinder
et l'enregistrer dans votre application JAX-RS. Le classeur spécifie comment l'injection de dépendance doit créer vos classes.
public class MyApplicationBinder extends AbstractBinder {
@Override
protected void configure() {
bind(MyService.class).to(MyService.class);
}
}
Lorsque @Inject
est détecté sur un paramètre ou un champ de type MyService.class
, il est instancié à l'aide de la classe MyService
. Pour utiliser ce classeur, il doit être enregistré avec l'application JAX-RS. Dans votre web.xml
, définissez une application JAX-RS comme suit:
<servlet>
<servlet-name>MyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.mypackage.MyApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MyApplication</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Implémentez la classe MyApplication
(spécifiée ci-dessus dans le init-param
).
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new MyApplicationBinder());
packages(true, "com.mypackage.rest");
}
}
Le classeur spécifiant l'injection de dépendance est enregistré dans le constructeur de la classe. Nous indiquons également à l'application où trouver les ressources REST (dans votre cas, MyResource
) à l'aide de l'appel de méthode packages()
.
D'abord, juste pour répondre à un commentaire dans la réponse acceptée.
"Que fait bind? Et si j'ai une interface et une implémentation?"
Il se lit simplement bind( implementation ).to( contract )
. Vous pouvez utiliser la chaîne alternative .in( scope )
. Portée par défaut de PerLookup
. Donc, si vous voulez un singleton, vous pouvez
bind( implementation ).to( contract ).in( Singleton.class );
Il y a aussi une RequestScoped
disponible
De plus, au lieu de bind(Class).to(Class)
, vous pouvez également bind(Instance).to(Class)
, qui sera automatiquement un singleton.
Ajout à la réponse acceptée
Pour ceux qui essaient de comprendre comment enregistrer votre implémentation AbstractBinder
dans votre web.xml (c’est-à-dire que vous n’utilisez pas ResourceConfig
), il semble que le classeur ne sera pas découvert lors de l’analyse des packages, c.-à-d.
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>
your.packages.to.scan
</param-value>
</init-param>
Ou ce soit
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
com.foo.YourBinderImpl
</param-value>
</init-param>
Pour que cela fonctionne, j'ai dû implémenter un Feature
:
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;
@Provider
public class Hk2Feature implements Feature {
@Override
public boolean configure(FeatureContext context) {
context.register(new AppBinder());
return true;
}
}
L'annotation @Provider
devrait permettre à Feature
d'être récupérée par l'analyse du package. Ou sans analyse de paquet, vous pouvez enregistrer explicitement la Feature
dans le web.xml
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
com.foo.Hk2Feature
</param-value>
</init-param>
...
<load-on-startup>1</load-on-startup>
</servlet>
Voir également:
et pour des informations générales tirées de la documentation Jersey
Outre la liaison de base dans la réponse acceptée, vous avez également des usines dans lesquelles vous pouvez avoir une logique de création plus complexe et également avoir accès aux informations de contexte de la demande. Par exemple
public class MyServiceFactory implements Factory<MyService> {
@Context
private HttpHeaders headers;
@Override
public MyService provide() {
return new MyService(headers.getHeaderString("X-Header"));
}
@Override
public void dispose(MyService service) { /* noop */ }
}
register(new AbstractBinder() {
@Override
public void configure() {
bindFactory(MyServiceFactory.class).to(MyService.class)
.in(RequestScoped.class);
}
});
Ensuite, vous pouvez injecter MyService
dans votre classe de ressources.
La réponse sélectionnée date d'un certain temps. Il n'est pas pratique de déclarer chaque liaison dans un classeur HK2 personnalisé . J'utilise Tomcat et je n'ai eu qu'à ajouter une dépendance. Même s'il a été conçu pour Glassfish, il s'intègre parfaitement dans d'autres conteneurs.
<dependency>
<groupId>org.glassfish.jersey.containers.glassfish</groupId>
<artifactId>jersey-gf-cdi</artifactId>
<version>${jersey.version}</version>
</dependency>
Assurez-vous également que votre conteneur est correctement configuré ( voir la documentation ).
En retard mais j'espère que cela aide quelqu'un.
J'ai mon JAX RS défini comme ceci:
@Path("/examplepath")
@RequestScoped //this make the diference
public class ExampleResource {
Ensuite, dans mon code, je peux enfin injecter:
@Inject
SomeManagedBean bean;
Dans mon cas, la SomeManagedBean
est un bean ApplicationScoped.
J'espère que cela aide à n'importe qui.
Oracle recommande d'ajouter l'annotation @Path à tous les types à injecter lors de la combinaison de JAX-RS avec CDI: http://docs.Oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm Bien que cela soit loin d’être parfait (par exemple, Jersey vous avertira au démarrage), j’ai décidé de suivre cette voie, ce qui m’évite de conserver tous les types pris en charge dans un classeur.
Exemple:
@Singleton
@Path("singleton-configuration-service")
public class ConfigurationService {
..
}
@Path("my-path")
class MyProvider {
@Inject ConfigurationService _configuration;
@GET
public Object get() {..}
}
Pour moi, cela fonctionne sans la AbstractBinder
si j'inclus les dépendances suivantes dans mon application Web (s'exécutant sur Tomcat 8.5, Jersey 2.27):
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey-version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
<artifactId>jersey-cdi1x</artifactId>
<version>${jersey-version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>${jersey-version}</version>
</dependency>
Cela fonctionne avec CDI 1.2/CDI 2.0 pour moi (en utilisant Weld 2/3 respectivement).
Si vous préférez utiliser Guice et que vous ne voulez pas déclarer toutes les liaisons, vous pouvez également essayer cet adaptateur: