Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency injection with Jersey 2.0

Starting from scratch without any previous Jersey 1.x knowledge, I'm having a hard time understanding how to setup dependency injection in my Jersey 2.0 project.

I also understand that HK2 is available in Jersey 2.0, but I cannot seem to find docs that help with Jersey 2.0 integration.

@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 up my resource, but as soon as I add @Inject to MyService, the framework 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 at GitHub: https://github.com/donaldjarmstrong/jaxrs

like image 650
donnie_armstrong Avatar asked Apr 25 '13 13:04

donnie_armstrong


People also ask

What is Jersey inject?

Jersey is a Java Framework that is commonly used to help generate REST Api. HK2 is a lightweight framework which allow Inversion of Control (IoC) and dependency injection (DI)

Does dependency injection improve performance?

The dependency injection (DI) has become an increasingly popular tool in Android development, and for good reason. Injections reduce the amount you have to code (and hence, debug), facilitating the creation of better apps and a smoother development process.

Does dependency injection affect performance?

There is a performance impact, but its effect will depend on many different variables. Every architectural decision involves research and weighing pros and cons, and ultimately boils down to a subjective opinion.


2 Answers

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

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

When @Inject is detected on a parameter or field of type MyService.class it is instantiated using the class MyService. To use this binder, it need to be registered with the JAX-RS application. In your web.xml, define a JAX-RS application like this:

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

Implement the MyApplication class (specified above in the init-param).

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

The binder specifying dependency injection is registered in the constructor of the class, and we also tell the application where to find the REST resources (in your case, MyResource) using the packages() method call.

like image 67
joscarsson Avatar answered Sep 21 '22 17:09

joscarsson


First just to answer a comment in the accepts answer.

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

It simply reads bind( implementation ).to( contract ). You can alternative chain .in( scope ). Default scope of PerLookup. So if you want a singleton, you can

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

There's also a RequestScoped available

Also, instead of bind(Class).to(Class), you can also bind(Instance).to(Class), which will be automatically be a singleton.


Adding to the accepted answer

For those trying to figure out how to register your AbstractBinder implementation in your web.xml (i.e. you're not using a ResourceConfig), it seems the binder won't be discovered through package 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 this either

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

To get it to work, I had to implement a 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 annotation should allow the Feature to be picked up by the package scanning. Or without package scanning, you can explicitly register the Feature in the 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:

  • Custom Method Parameter Injection with Jersey
  • How to inject an object into jersey request context?
  • How do I properly configure an EntityManager in a jersey / hk2 application?
  • Request Scoped Injection into Singletons

and for general information from the Jersey documentation

  • Custom Injection and Lifecycle Management

UPDATE

Factories

Aside from the basic binding in the accepted answer, you also have factories, where you can have more complex creation logic, and also have access to 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);     } }); 

Then you can inject MyService into your resource class.

like image 29
Paul Samsotha Avatar answered Sep 20 '22 17:09

Paul Samsotha