Getting Started with JSR 330: Dependency Injection for Java in Quarkus

Rolando Santamaría Masó
4 min readApr 2, 2023

--

Photo by Karolina Grabowska: https://www.pexels.com/photo/close-up-of-lego-blocks-4887167/

Introduction

Dependency Injection (DI) is a design pattern that promotes loose coupling between components by delegating the responsibility of object creation and management to a dedicated container or framework.

JSR 330, also known as “Dependency Injection for Java,” is a standard that defines a set of annotations and APIs for using DI in Java-based applications. Quarkus, a modern Java framework, supports JSR 330 out of the box, making it easy to use dependency injection in your projects.

This tutorial will guide you through the basics of JSR 330 in Quarkus and help you understand how to use it in your projects.

Prerequisites

  • Basic understanding of Java programming and object-oriented concepts
  • A Java development environment (JDK and an IDE or text editor)
  • Familiarity with build tools like Maven
  • Basic knowledge of Quarkus

Setting up the Project

To create a new Quarkus project, you can use the Quarkus Maven plugin, Quarkus CLI, or the Quarkus project initializer. In this tutorial, we will use the Quarkus Maven plugin.

  • Open a terminal and run the following command:
mvn io.quarkus:quarkus-maven-plugin:create \
-DprojectGroupId=com.example \
-DprojectArtifactId=jsr330-quarkus \
-DclassName="com.example.GreetingResource" \
-Dpath="/hello"
  • Change to the newly created project directory:
cd jsr330-quarkus

Overview of JSR 330 in Quarkus

Quarkus uses the CDI (Contexts and Dependency Injection) standard, which is built upon JSR 330.

The main JSR 330 annotations you will use in Quarkus are:

  • Inject
  • Qualifier
  • Singleton
  • Named

Let’s explore each of these annotations through examples in the following sections.

Using @Inject

The @Inject annotation is used to indicate that a particular constructor, field, or method requires dependency injection.

  • Create a new Java class named GreetingService in the com.example package:
package com.example;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class GreetingService {

public String greet(String name) {
return "Hello, " + name + "!";
}
}
  • Update the GreetingResource class to inject the GreetingService:
package com.example;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;

@Path("/hello")
public class GreetingResource {

@Inject
GreetingService greetingService;

@GET
public String hello(@QueryParam("name") String name) {
return greetingService.greet(name);
}
}

In this example, we created a GreetingService class and used the @Inject annotation to inject an instance of it into the GreetingResource class. When the hello method is called, it delegates to the injected GreetingService to generate the greeting.

Using @Qualifier and @Named

The @Qualifier and @Named annotations are used to disambiguate between multiple implementations of the same interface or class when injecting a dependency.

  • Create a new Java interface named Translator in the com.example package:
package com.example;

public interface Translator {
String translate(String text);
}
  • Create two implementations of the Translator interface:
package com.example;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

@ApplicationScoped
@Named("english")
public class EnglishTranslator implements Translator {

@Override
public String translate(String text) {
return "Translated to English: " + text;
}
}
package com.example;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

@ApplicationScoped
@Named("spanish")
public class SpanishTranslator implements Translator {

@Override
public String translate(String text) {
return "Traducido al español: " + text;
}
}
  • Update the GreetingResource class to inject both implementations of the Translator interface:
package com.example;

import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;

@Path("/hello")
public class GreetingResource {

@Inject
GreetingService greetingService;

@Inject
@Named("english")
Translator englishTranslator;

@Inject
@Named("spanish")
Translator spanishTranslator;

@GET
public String hello(@QueryParam("name") String name, @QueryParam("lang") String lang) {
String greeting = greetingService.greet(name);
if ("es".equalsIgnoreCase(lang)) {
return spanishTranslator.translate(greeting);
} else {
return englishTranslator.translate(greeting);
}
}
}

In this example, we created an interface Translator and two implementations, EnglishTranslator and SpanishTranslator. We used the @Named annotation to provide a unique name for each implementation. In the GreetingResource class, we injected both implementations using the @Named annotation to specify which implementation to use.

Conclusion

In this tutorial, you learned the basics of using JSR 330 in Quarkus to implement dependency injection in your Java applications. You learned how to use the @Inject, @Qualifier, and @Named annotations to manage dependencies and inject different implementations of an interface.

Remember, by using dependency injection, developers can create more modular, testable, and maintainable Java applications.

It’s worth mentioning that numerous Java frameworks, in addition to Quarkus, implement the JSR 330 specification. Some of these frameworks include, but are not limited to:

  • Spring Framework
  • Google Guice
  • Dagger
  • Micronaut

Continue reading:

--

--