Introduction to GlassFish HK2
This page describes the GlassFish HK2 3.0 API, which is based on Jakarta Dependency Injection (DI) standard annotations.
Also, Habitat has been replaced with a new interface called ServiceLocator.
GlassFish HK2 is a declarative framework for services using annotations like Contract and Service.
This page is intended to show simple usages of GlassFish HK2 mainly using the standard Jakarta DI API.
For information about using the GlassFish HK2 programmatic API see this page.
For information about GlassFish HK2 extensibility options see this page
For information about Jakarta DI see this site.
This page assumes that you are using the GlassFish HK2 provided ability to automatically find and
register services in an GlassFish HK2 registry. For more information on how to control what services
are automatically bound to what registries see TBD.
Getting Started
In order to mark a concrete implementation class as one that should be available
as a service you annotate your class with @Service.
@Service
public class Foo {
}
By default Foo will be advertised as itself and by any interfaces that are marked with @Contract.
Lets make Foo an interface that is a Contract and create an implementation of Foo:
@Contract
public interface Foo {
}
@Service
public class FooImpl implements Foo {
}
The FooImpl class will be placed into the registry advertised under both FooImpl and Foo.
Named Services
In order to differentiate different implementations of the same interface you can name your services.
Here is an example of a class that implements a contract and has two implementations, both named differently:
@Contract
public interface Book {
}
@Service @Named
public class MobyDick implements Book {
}
@Service @Named
public class ParadiseLost implements Book {
}
The two classes, MobyDick and ParadiseLost, will be added to the service registry with the names
“MobyDick” and “ParadiseLost”. If you use the Named
qualifier without specifying a name then the name you get is the class name without the package.
Qualified Services
Services can also be qualified with annotations called qualifiers. Qualifiers are annotations that are themselves
annotated with @Qualifier. Here is an
example contract with three implementations:
@Contract
public interface Color {
}
@Service @Blue
public class BlueColor implements Color {
}
@Service @Red
public class RedColor implements Color {
}
@Service @Yellow
public class YellowColor implements Color {
}
The Blue annotation is defined like this:
@Qualifier
@Retention(RUNTIME)
@Target( { TYPE, METHOD, FIELD, PARAMETER })
public @interface Blue {
}
It is an exercise left up to the reader to implement the Red and Yellow annotations.
Basic Injection
Lets make our example a little more interesting by injecting a book into Foo. This is done using the
Jakarta DI standard Inject annotation:
@Service
public class FooImpl implements Foo {
@Inject
private Book book;
...
}
Upon construction the book field will be filled in by GlassFish HK2. You can also inject into the constructor of FooImpl, or
use an initializer method. In both of those cases the constructor or method must be annotated with
@Inject in order to tell GlassFish HK2 which is the proper constructor or method to call.
Here is FooImpl implemented with constructor injection:
@Service
public class FooImpl implements Foo {
private final Book book;
@Inject
public FooImpl(Book book) {
// constructor injected!
this.book = book;
}
}
Here is FooImpl implemented with initializer method injection:
@Service
public class FooImpl implements Foo {
private Book book;
@Inject
public void setBook(Book book) {
// initializer method injected!
this.book = book;
}
}
In all three of the above cases (field injected, constructor injected or initializer method injected) the injection will occur
prior to the postConstruct method of FooImpl. In this example we use the injected book in the postConstruct method:
@Service
public class FooImpl implements Foo {
@Inject
private Book book;
@PostConstruct
private void postConstruct() {
book.doSomething();
}
}
Injection by name
The astute observer of our example will have noticed that when injecting a Book into FooImpl that we never selected which book we wanted.
That can be fixed by using the Named qualifier at the point of injection. Lets fix the example by injecting both of the Books we defined earlier:
@Service
public class FooImpl implements Foo {
@Inject @Named("MobyDick")
private Book mobyDick;
@Inject @Named("ParadiseLost")
private Book paradiseLost;
}
The implementation of Book given the name “MobyDick” will be injected into the mobyDick field, and the implementation of
Book given the name “ParadiseLost” will be injected into the paradiseLost field.
Injection by qualifier
Injections can also be more specifically chosen by using qualifiers. In the previous example we created three implementations
of Color, each of which was qualified with a qualifier. Here we create a class called ColorMixer which injects the colors
in an initializer method, which also demonstrates that an initializer method (or constructor) can take more than one
parameter:
@Service
public class ColorMixer {
private Color red;
private Color blue;
private Color yellow;
@Inject
private void addPrimaries(
@Red Color red,
@Blue Color blue,
@Yellow Color yellow) {
this.red = red;
this.blue = blue;
this.yellow = yellow;
}
}
Note that the qualifiers can go on the parameters of the initializer method addPrimaries. In the above example the RedColor,
BlueColor and YellowColor services will be injected into the proper fields of the initializer.
Provider injection
There are times when your code would like finer control over when a instance of a service is created.
Anywhere that you can inject a service, you can also inject a Provider.
When you inject a Provider for a service rather than
the service itself the system will potentially delay the creation of the service until the
get method of the Provider has been called.
Lets go back to our ColorMixer example, and inject providers (into fields this time) for our primaries.
@Service
public class ColorMixer {
@Inject @Red
private Provider<Color> redProvider;
@Inject @Blue
private Provider<Color> blueProvider;
@Inject @Yellow
private Provider<Color> yellowProvider;
}
This service can then get the color implementations later. In this method of ColorMixer we create purple by getting the
red and blue colors:
@Service
public class ColorMixer {
...
public Color makePurple() {
return mix(redProvider.get(), blueProvider.get());
}
}
Note that if no-one ever makes a color that involves using yellow, that the YellowColor implementation class will never
be created, since no-one ever called the get method of the yellowProvider field.
The value passed into any Provider injection point will be an
instance of IterableProvider.
IterableProvider injection
It is often the case that a single contract has more than one implementation. Sometimes it is useful to get access to
all of the implementations of the contract.
This can be done by using an IterableProvider.
IterableProvider extends Provider and also implements Iterable.
Anywhere a service can be injected an IterableProvider for that service can be injected.
In this example we create a Library service that wants to be able to list all of the books it carries:
@Service
public class Library {
@Inject
private IterableProvider<Book> allBooks;
public LinkedList<Book> getAllBooks() {
LinkedList<Book> retVal = new LinkedList<Book>();
for (Book book : allBooks) {
retVal.add(book);
}
return retVal;
}
}
Since IterableProvider implements Iterable
it can be used in Java for/while loops, as demonstrated in the above example.
Another feature of the IterableProvider is that it can
be used to further narrow down the selection criteria at run time.
In our above example we can progrommatically choose the book we are interested
in based on a name passed into a method. Here is how it would look:
@Service
public class Library {
@Inject
private IterableProvider<Book> allBooks;
public Book findBook(String name) {
return allBooks.named(name).get();
}
}
In the above example we call the named method IterableProvider
in order to select the book with the given name.
The call to get then just returns the book with the given name.
Iterable injection
Iterable can be used as an injection point rather than IterableProvider. The following code will
work as expected:
@Service
public class Library {
@Inject
private Iterable<Book> allBooks;
public LinkedList<Book> getAllBooks() {
LinkedList<Book> retVal = new LinkedList<Book>();
for (Book book : allBooks) {
retVal.add(book);
}
return retVal;
}
}
The value passed into any Iterable injection point will be an
instance of IterableProvider.
Conclusion
The majority of usages of GlassFish HK2 should use standard Jakarta DI annotations along with
@Service and @Contract.
In some cases code will also use IterableProvider as outlined above.
In even rarer cases the code may need extremely specialized control over GlassFish HK2 by using the
programmatic API, as described here.
We have gone through many simple examples which have shown the basic functionality of GlassFish HK2 and JSR-330 in your
applications. Hopefully they have provided you with knowledge about how these annotations work and how they
can be used in your own applications.