Dependency Injection (JSR 330) on the Java platform using HK2
HK2
is an implementation ofJSR-330
in a JavaSE environment.
JSR-330
defines services and injection points that can be dynamically discovered at runtime and which allow forInversion of Control
(IoC) anddependency injection
(DI).
Introduction
Most developers are familiar with using the Spring Container for dependency injection,
in this post we are going to look at dependency injection using HK2
.
Prerequisites
To follow along with this post, your development environment must satisfy the following requirements:
Project Structure
At the end of this post, you should have a project structure similar to the one illustrated below:
.
|__src/
| |__main/
| | |__java/
| | | |__com/
| | | | |__juliuskrah/
| | | | | |__gs/
| | | | | | |__Application.java
| | | | | | |__MessagePrinter.java
| | | | | | |__MessageService.java
| | | | | | |__MessageServiceImpl.java
|__pom.xml
You can also find the complete sample in the github repository.
Project Dependencies
To get started, we need to gather our project dependencies. This is a maven based project, and we can easily declare
our dependencies in a pom.xml
file.
file:
pom.xml
We have declared two dependencies, hk2-locator
and hk2-metadata-generator
. The first dependency contains the hk2
library and its transitive dependencies. We will talk about the second dependency later in this post.
HK2 Services and Injection
To demonstrate dependency injection using HK2, we will create a concrete Service
class MessagePrinter
and inject
a Contract
interface MessageService
. The MessagePrinter
will print messages delivered via the MessageService
:
file:
src/main/java/com/juliuskrah/gs/MessageService.java
The MessageService
interface defines one contract getMessage
which returns a String
. The MessagePrinter
will
inject an instance of MessageService
and print its messages:
file:
src/main/java/com/juliuskrah/gs/MessagePrinter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
@Service
public class MessagePrinter {
private MessageService messageService;
@Inject
public MessagePrinter(MessageService messageService) {
this.messageService = messageService;
}
public void printMessage() {
System.out.println(this.messageService.getMessage());
}
}
On line 5 we use constructor injection to inject the MessageService
instance.
Next we create a concrete service implementation of MessageService
. It is this service implementation that
HK2 will inject into the MessageService
contract.
file:
src/main/java/com/juliuskrah/gs/MessageServiceImpl.java
Now that we have our services and contract set up, we wire it all together using HK2.
In order for HK2 to automatically find services at runtime it can read files called inhabitant files
. These are
usually placed in your JAR file at location META-INF/hk2-locator
. Normally the file is named default
. (You can
however use a different file name or location(s) by using more specific API). HK2 has a tool for automatically
creating these files based on class files annotated with @Service
. There is also a simple API for creating and
populating a ServiceLocator
with services found in these files. This is where the second dependency we declared
earlier comes into play.
The HK2 Metadata Generator will generate hk2 inhabitant files during the compilation of your java files. It is a
JSR-269
annotation processor that handles the @Service
annotation. The only requirement for using it is to put
the javax.inject
, hk2-utils
, hk2-api
and hk2-metadata-generator
libraries in the classpath of the javac
process.
In this post we use the HK2 Metadata Generator
in a Maven based build system:
Since Maven uses transitive dependencies this is all you need to add as a dependency during build.
In order to have our program automatically load the files generated with the hk2-inhabitant-generator you can use the
createAndPopulateServiceLocator
method near the start of our main method, like this:
file:
src/main/java/com/juliuskrah/gs/Application.java
Just like that, we are done
Conclusion
In this post we learned how to implement Dependency Injection using HK2. We demonstrated our example using services
and contracts and constructor based injection.
You can find the source to this guide in the github repository. Until the next post, keep doing cool things .