Jersey uses HK2 which is a lightweight framework to provide Inversion of Control and Dependency Injection
Create the following folder structure:

pom.xml: (if using Gradle, replace pom.xml with build.gradle)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.maxjava</groupId> <artifactId>FirstJerseyWebService</artifactId> <version>1.0</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>org.glassfish.jersey.containers</groupId> <artifactId>jersey-container-servlet-core</artifactId> <version>2.29.1</version> </dependency> <dependency> <groupId>org.glassfish.jersey.inject</groupId> <artifactId>jersey-hk2</artifactId> <version>2.29.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>11</source> <target>11</target> </configuration> </plugin> </plugins> </build> </project>
plugins { id 'java' id 'war' } sourceCompatibility = 11 targetCompatibility = 11 group 'net.maxjava' version '1.0' repositories { mavenCentral() } dependencies { implementation group: 'org.glassfish.jersey.containers', name: 'jersey-container-servlet-core', version: '2.29.1' implementation group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: '2.29.1' }
Some notes about pom.xml:
- jersey-container-servlet-core and jersey-hk2 is needed for using Jersey as servlet
- maven-compiler-plugin is for compiling the code using source version 11 and target version 11
- A war artifact will be created for deployment to servlet containers
Some notes about gradle.build:
- jersey-container-servlet-core and jersey-hk2 is needed for using Jersey as servlet
- targetCompatibility is used to configure compilation with source version 11 and target version 11
- A war artifact will be created for deployment to servlet containers
web.xml:
<web-app> <display-name>Jersey Web Service with Injection</display-name> <servlet> <servlet-name>JerseyWebServiceInjection</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>application.MyApplication</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>JerseyWebServiceInjection</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
MyApplication.java:
package application; import binder.MyBinder; import org.glassfish.jersey.server.ResourceConfig; import resource.MyResource; public class MyApplication extends ResourceConfig { public MyApplication() { register(MyResource.class); register(new MyBinder()); } }
Some notes about MyApplication.java:
- This is main configuration class and has to be specified in web.xml
- In the constructor, register the resource classes, which are annotated with JAX-RS for web services, and register the binder classes, which extend AbstractBinder for configuring the bindings
MyBinder.java:
package binder; import org.glassfish.jersey.internal.inject.AbstractBinder; import service.MySquare; public class MyBinder extends AbstractBinder { @Override protected void configure() { bindAsContract(MySquare.class); } }
Some notes about MyBinder.java:
- It extends AbstractBinder and overrides the configure method
- Inside configure it calls bindAsContract to bind MySquare. Default scope is singleton meaning only a single MySquare object will be shared by all injected location
MyResource.java:
package resource; import service.MySquare; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @Path("/myapp") public class MyResource { @Inject MySquare mySquare; @GET @Path("/side") @Produces(MediaType.TEXT_PLAIN) public Response getNumberOfSides() { return Response.ok("Number of sides: " + mySquare.getNumberofSides()).status(Response.Status.OK).build(); } }
Some notes about MyResource.java:
- Use @Inject to inject class MySquare
MySquare.java:
package service; public class MySquare { public int getNumberofSides() { return 4; } }
Sample output:
curl --request GET '<CONTEXT_PATH>/myapp/side'
Response:
Number of sides: 4
Instead of binding to a class directly, binding target can be an interface
Add the interface MyShape.java like the following:

MyShape.java:
package service; public interface MyShape { int getNumberofSides(); }
Change MySquare.java:
package service; public class MySquare implements MyShape { public int getNumberofSides() { return 4; } }
Change MyBinder.java:
package binder; import org.glassfish.jersey.internal.inject.AbstractBinder; import service.MyShape; import service.MySquare; public class MyBinder extends AbstractBinder { @Override protected void configure() { bind(MySquare.class).to(MyShape.class); } }
Change MyResource.java:
package resource; import service.MyShape; import service.MySquare; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @Path("/myapp") public class MyResource { @Inject MyShape myShape; @GET @Path("/side") @Produces(MediaType.TEXT_PLAIN) public Response getNumberOfSides() { return Response.ok("Number of sides: " + myShape.getNumberofSides()).status(Response.Status.OK).build(); } }
Sample output:
curl --request GET '<CONTEXT_PATH>/myapp/side'
Response:
Number of sides: 4
If there is more than 1 implementation of the interface, we can use @Named to specify the implementation
Let's add MyTriangle.java as another implementation of MyShape like the following:

Add MyTriangle.java:
package service; public class MyTriangle implements MyShape { public int getNumberofSides() { return 3; } }
Change MyBinder.java:
package binder; import org.glassfish.jersey.internal.inject.AbstractBinder; import service.MyShape; import service.MySquare; import service.MyTriangle; public class MyBinder extends AbstractBinder { @Override protected void configure() { bind(MyTriangle.class).named("Triangle").to(MyShape.class); bind(MySquare.class).named("Square").to(MyShape.class); } }
Change MyResource.java:
package resource; import service.MyShape; import service.MySquare; import javax.inject.Inject; import javax.inject.Named; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @Path("/myapp") public class MyResource { @Inject @Named("Triangle") MyShape myTriangle; @Inject @Named("Square") MyShape mySquare; @GET @Path("/side") @Produces(MediaType.TEXT_PLAIN) public Response getNumberOfSides() { return Response.ok("Number of sides for myTriangle: " + myTriangle.getNumberofSides() + ", Number of sides for mySquare: " + mySquare.getNumberofSides()).status(Response.Status.OK).build(); } }
Sample output:
curl --request GET '<CONTEXT_PATH>/myapp/side'
Response:
Number of sides for myTriangle: 3, Number of sides for mySquare: 4