Getting Started with JSR 330: Dependency Injection for Java in Quarkus
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 thecom.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 theGreetingService
:
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 thecom.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 theTranslator
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: