Ich beginne ohne jegliche vorherige Jersey 1.x-Kenntnisse von Grund auf und verstehe, wie ich die Abhängigkeitsinjektion in meinem Jersey 2.0-Projekt einrichten kann.
Ich verstehe auch, dass HK2 in Jersey 2.0 verfügbar ist, aber ich finde anscheinend keine Dokumente, die bei der Integration von Jersey 2.0 hilfreich sind.
@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>
Ich kann den Container dazu bringen, meine Ressource zu starten und bereitzustellen, aber sobald ich @Inject zu MyService hinzufüge, löst das Framework eine Ausnahme aus:
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)
Mein Starterprojekt ist unter GitHub verfügbar: https://github.com/donaldjarmstrong/jaxrs
Sie müssen eine AbstractBinder
definieren und in Ihrer JAX-RS-Anwendung registrieren. Die Sammelmappe gibt an, wie die Abhängigkeitsinjektion Ihre Klassen erstellen soll.
public class MyApplicationBinder extends AbstractBinder {
@Override
protected void configure() {
bind(MyService.class).to(MyService.class);
}
}
Wenn @Inject
für einen Parameter oder ein Feld des Typs MyService.class
erkannt wird, wird er mit der Klasse MyService
instanziiert. Um diese Mappe verwenden zu können, muss sie bei der JAX-RS-Anwendung registriert sein. Definieren Sie in Ihrem web.xml
eine JAX-RS-Anwendung wie folgt:
<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>
Implementieren Sie die MyApplication
-Klasse (oben im init-param
angegeben).
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new MyApplicationBinder());
packages(true, "com.mypackage.rest");
}
}
Die bindungsspezifizierende Abhängigkeitseinspritzung wird im Konstruktor der Klasse registriert. Außerdem wird der Anwendung mit Hilfe des Methodenaufrufs packages()
mitgeteilt, wo die Ressourcen REST (in Ihrem Fall MyResource
) zu finden sind.
Um nur einen Kommentar in der Antwort anzunehmen.
"Was macht bind? Was ist, wenn ich eine Schnittstelle und eine Implementierung habe?"
Es liest einfach bind( implementation ).to( contract )
. Sie können die alternative Kette .in( scope )
. Standardbereich von PerLookup
. Wenn Sie also ein Singleton wollen, können Sie
bind( implementation ).to( contract ).in( Singleton.class );
Es ist auch eine RequestScoped
verfügbar
Anstelle von bind(Class).to(Class)
können Sie auch bind(Instance).to(Class)
verwenden, die automatisch ein Singleton sein wird.
Hinzufügen zur akzeptierten Antwort
Für diejenigen, die herausfinden möchten, wie Sie Ihre AbstractBinder
-Implementierung in Ihrer web.xml registrieren (d. H. Sie verwenden keine ResourceConfig
), scheint es, als würden die Ordner nicht durch das Scannen von Paketen entdeckt, d. H.
<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>
Oder das auch
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
com.foo.YourBinderImpl
</param-value>
</init-param>
Damit es funktioniert, musste ich eine Feature
implementieren:
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;
}
}
Die @Provider
-Annotation sollte es ermöglichen, dass die Feature
vom Paketscan abgeholt wird. Ohne das Scannen von Paketen können Sie die Feature
explizit im web.xml
registrieren.
<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>
Siehe auch:
und für allgemeine Informationen aus der Jersey-Dokumentation
Abgesehen von der grundlegenden Bindung in der akzeptierten Antwort gibt es auch Fabriken, in denen Sie eine komplexere Erstellungslogik haben können und auch Zugriff auf Kontextinformationen anfordern können. Zum Beispiel
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);
}
});
Dann können Sie MyService
in Ihre Ressourcenklasse einfügen.
Die ausgewählte Antwort stammt aus einer Weile zurück. Es ist nicht praktisch, jede Bindung in einer benutzerdefinierten HK2-Mappe zu deklarieren. Ich benutze Tomcat und musste nur eine Abhängigkeit hinzufügen. Obwohl es für Glassfish entworfen wurde, passt es perfekt in andere Behälter.
<dependency>
<groupId>org.glassfish.jersey.containers.glassfish</groupId>
<artifactId>jersey-gf-cdi</artifactId>
<version>${jersey.version}</version>
</dependency>
Stellen Sie sicher, dass auch Ihr Container richtig konfiguriert ist ( siehe Dokumentation ).
Spät aber ich hoffe das hilft jemandem.
Ich habe meinen JAX RS folgendermaßen definiert:
@Path("/examplepath")
@RequestScoped //this make the diference
public class ExampleResource {
Dann kann ich endlich in meinem Code spritzen:
@Inject
SomeManagedBean bean;
In meinem Fall ist SomeManagedBean
eine ApplicationScoped-Bean.
Hoffe, das hilft jedem.
Oracle empfiehlt, die @Path-Annotation zu allen Typen hinzuzufügen, die injiziert werden sollen, wenn JAX-RS mit CDI kombiniert wird: http://docs.Oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm Obwohl dies alles andere als perfekt ist (z. B. werden Sie beim Start von Jersey gewarnt), entschied ich mich für diesen Weg, der mich davon abhält, alle unterstützten Typen in einer Sammelmappe zu behalten.
Beispiel:
@Singleton
@Path("singleton-configuration-service")
public class ConfigurationService {
..
}
@Path("my-path")
class MyProvider {
@Inject ConfigurationService _configuration;
@GET
public Object get() {..}
}
Für mich funktioniert es ohne die AbstractBinder
, wenn ich in meiner Webanwendung (unter Tomcat 8.5, Jersey 2.27) die folgenden Abhängigkeiten aufnehme:
<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>
Es funktioniert für mich mit CDI 1.2/CDI 2.0 (jeweils mit Weld 2/3).
Wenn Sie lieber Guice verwenden und nicht alle Bindungen deklarieren möchten, können Sie auch diesen Adapter versuchen: