Injection de dépendance avec Jersey 2.0

à partir de zéro sans aucun Maillot antérieur 1.x connaissance, j'ai du mal à comprendre comment configurer l'injection de dépendances dans mon projet Jersey 2.0.

je comprends également que HK2 est disponible à Jersey 2.0, mais je ne semble pas trouver des docs qui aident à L'intégration Jersey 2.0.

java prettyprint-override">@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 obtenir le conteneur pour démarrer et servir ma ressource, mais dès que je ajouter @Inject à MyService, le cadre jette 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 chez GitHub: https://github.com/donaldjarmstrong/jaxrs

93
demandé sur Paul Samsotha 2013-04-25 17:58:50

6 réponses

vous devez définir un AbstractBinder et l'enregistrer dans votre application JAX-RS. Le binder spécifie comment l'injection de dépendances devrait 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é en utilisant 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 ceci:

<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>

mettre en œuvre 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 binder spécifiant l'injection de dépendances est enregistré dans le constructeur de la classe, et nous indiquons également à l'application où trouver les autres ressources (dans votre cas, MyResource ) en utilisant la méthode packages() .

91
répondu joscarsson 2015-11-05 18:13:05

juste pour répondre à un commentaire dans l'accepte réponse.

"Ce qui ne se lient faire? Que faire si j'ai une interface et une implémentation?"

Il lit simplement bind( implementation ).to( contract ) . Vous pouvez alternative chaîne .in( scope ) . Champ d'application par défaut de PerLookup . Donc, si vous voulez un singleton, vous pouvez

bind( implementation ).to( contract ).in( Singleton.class );

il y a aussi un RequestScoped disponible

aussi, à la place de bind(Class).to(Class) , vous pouvez aussi bind(Instance).to(Class) , qui sera automatiquement être un singleton.


ajout à la réponse acceptée

pour ceux qui essaient de comprendre comment enregistrer votre implémentation AbstractBinder dans votre site web.xml (c'est-à-dire que vous n'utilisez pas un ResourceConfig ), il semble que le binder ne sera pas découvert par la numérisation du paquet, c'est-à-dire

<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 le faire fonctionner, j'ai dû mettre en place 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 doit permettre au Feature d'être capté par le balayage du paquet. Ou sans balayage de paquet, vous pouvez enregistrer explicitement le 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 Aussi:

et pour des informations générales de la documentation De Jersey


mise à jour

usines

outre la reliure de base dans la réponse acceptée, vous avez aussi des usines, où vous pouvez avoir une logique de création plus complexe, et aussi avoir accès à des informations de contexte de demande. Pour 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.

44
répondu Paul Samsotha 2017-09-23 02:29:43

la réponse choisie date d'il y a longtemps. Il n'est pas pratique de déclarer chaque reliure dans un classeur hk2 personnalisé. J'utilise Tomcat et j'ai juste dû ajouter une dépendance. Même s'il a été conçu pour les poissons en verre, il s'adapte 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 que votre conteneur est correctement configuré ( voir la documentation ).

11
répondu otonglet 2015-01-08 19:49:19

en retard mais j'espère que cela aidera quelqu'un.

j'ai mon JAX RS défini comme ceci:

@Path("/examplepath")
@RequestScoped //this make the diference
public class ExampleResource {

puis, dans mon code enfin je peux injecter:

@Inject
SomeManagedBean bean;

dans mon cas, le SomeManagedBean est un haricot dopé.

Espérons que cette aide à la personne.

5
répondu gjijon 2016-10-06 07:51:52

Oracle recommande d'ajouter l'annotation @Path à tous les types à injecter lors de la combinaison JAX-RS avec CDI: http://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm Bien que ce soit loin d'être parfait (par exemple vous recevrez un avertissement De Jersey au démarrage), j'ai décidé de prendre cette route, ce qui m'évite de maintenir tous les types supportés dans un classeur.

exemple:

@Singleton
@Path("singleton-configuration-service")
public class ConfigurationService {
  .. 
}

@Path("my-path")
class MyProvider {
  @Inject ConfigurationService _configuration;

  @GET
  public Object get() {..}
}
2
répondu Benjamin Mesing 2015-08-24 09:28:18

si vous préférez utiliser Guice et que vous ne voulez pas déclarer toutes les fixations, vous pouvez également essayer cet adaptateur:

guice-pont-jit-injecteur

0
répondu Choi 2014-07-03 20:18:36