Injection Injection with Jersey 2.0

Starting from scratch without any knowledge last year from Jersey 1.x, it's hard for me to figure out how to set up dependency injection in a Jersey 2.0 project.

I also understand that HK2 is available in Jersey 2.0, but I cannot find documents that help integration with 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> 

I can get the container to start and serve my resource, but as soon as I add @Inject to MyService, the structure throws an 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) 


My starter project is available on GitHub: https://github.com/donaldjarmstrong/jaxrs

+95
java dependency-injection jersey hk2
Apr 25 '13 at 13:58
source share
7 answers

You need to define AbstractBinder and register it in the JAX-RS application. A binder determines how dependency injection should create your classes.

 public class MyApplicationBinder extends AbstractBinder { @Override protected void configure() { bind(MyService.class).to(MyService.class); } } 

When @Inject detected in a parameter or field of type MyService.class , it is created using the MyService class. To use this binder, it must be registered in the JAX-RS application. In your web.xml define a JAX-RS application as follows:

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

MyApplication class (indicated above in init-param ).

 public class MyApplication extends ResourceConfig { public MyApplication() { register(new MyApplicationBinder()); packages(true, "com.mypackage.rest"); } } 

A binder is defined in the class constructor that defines the dependency injection, and we also tell the application where to find REST resources (in your case, MyResource ) by calling the packages() method.

+95
Jun 16 '13 at 11:59
source share

First, just reply to the comment in the accepts answer.

"What does the binding do? What if I have an interface and implementation?"

It just reads bind( implementation ).to( contract ) . You can use an alternative .in( scope ) chain. The default PerLookup . Therefore, if you want a singleton, you can

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

Also has RequestScoped

In addition, instead of bind(Class).to(Class) you can also bind(Instance).to(Class) , which will be automatically single.




Adding to accepted answer

For those who are trying to figure out how to register the AbstractBinder implementation in your web.xml (i.e. you are not using ResourceConfig ), it seems that the binder will not be detected through packet scanning, i.e.

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

Or is it either

 <init-param> <param-name>jersey.config.server.provider.classnames</param-name> <param-value> com.foo.YourBinderImpl </param-value> </init-param> 

To make it work, I had to implement 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; } } 

The @Provider should allow Feature to be displayed when scanning a package. Or without scanning packages, you can explicitly register Feature in 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> 



See also:

and for general information from the jersey documentation




UPDATE

Factories

In addition to the main binding in the accepted answer, you also have factories where you can have more complex creation logic and also have access to the request context information. for example

 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); } }); 

You can then introduce MyService into your resource class.

+45
Mar 26 '15 at 10:07
source share

The selected answer dates back some time. It is not practical to declare each binding in a normal HK2 binder. I am using Tomcat and I just need to add one dependency. Despite being designed for Glassfish, it fits perfectly in other containers.

  <dependency> <groupId>org.glassfish.jersey.containers.glassfish</groupId> <artifactId>jersey-gf-cdi</artifactId> <version>${jersey.version}</version> </dependency> 

Make sure the container is configured correctly ( see documentation ).

+11
Jan 08 '15 at 19:49
source share

Late, but I hope this helps someone.

My JAX RS is defined as follows:

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

Then, in my code, finally, I can enter:

 @Inject SomeManagedBean bean; 

In my case, SomeManagedBean is an ApplicationScoped bean.

Hope this helps anyone.

+5
Oct 06 '16 at 7:51
source share

Oracle recommends adding @Path annotation to all types that you need to enter when combining JAX-RS with CDI: http://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm Although this is far from ideal (for example, you get a warning from Jersey at startup), I decided to use this route, which will save me from supporting all supported types inside the binder.

Example:

 @Singleton @Path("singleton-configuration-service") public class ConfigurationService { .. } @Path("my-path") class MyProvider { @Inject ConfigurationService _configuration; @GET public Object get() {..} } 
+2
Jun 02 '14 at 13:52
source share

If you prefer to use Guice and don't want to declare all bindings, you can also try this adapter:

guice-bridge-jit-injector

0
Jul 03 '14 at 20:18
source share

For me, this works without AbstractBinder if I include the following dependencies in my web application (works on 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> 

It works with CDI 1.2 / CDI 2.0 for me (using Weld 2/3, respectively).

0
Jan 9 '19 at 10:26
source share



All Articles