HK2: The Hundred Kilobyte Kernel
Originally published on 06 Jun 2019
Last updated on 08 Aug 2019
HK2 is a rather old dependency injection (DI) framework and is used as the core of Payara Server. Created in 2007 by Kohsuke Kawaguchi (who is also the creator of the Hudson project, now Jenkins) at Sun Microsystems, it followed JSR 330 closely, which was the JSR that introduced the @Inject, @Named and @Qualifier annotations, the very annotations which are also heavily used in CDI.
Basic Usage of HK2
Basic Usage At its most basic, @Service public class Bar { @PostConstruct public void postConstuct() { System.out.println("postConstruct of class Bar"); } public String doStuff() { return "A string returned from class Bar"; } } @Service public class Foo { @Inject private Bar bar; public void stuffDone() { System.out.println(bar.doStuff()); } }
Note that the code above uses a combination of the JSR 330 annotation @Inject, the common annotation @PostConstruct, and HK2's own annotation @Service. This last annotation essentially marks a class for automatic inclusion in HK2's repository of managed classes, called the "service locator".
To allow HK2 to find these @Service annotated classes (simply called services) at runtime it reads a file called the inhabitants file, which is normally located at META-INF/hk2-locator/default. This can be generated using a maven plugin that scans for these annotations during the build process, i.e.:
<build> <plugins> <plugin> <groupId>org.glassfish.hk2</groupId> <artifactId>hk2-inhabitant-generator</artifactId> <executions> <execution> <id>generate-inhabitants</id> <goals> <goal>generate-inhabitants</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
The following shows an example fragment of what such generated file looks like (this example is taken from Payara Server):
public static void main(String[] args) { ServiceLocator serviceLocator = ServiceLocatorUtilities.createAndPopulateServiceLocator(); Foo foo = serviceLocator.getService(Foo.class); foo.stuffDone(); }
The interesting thing to note here is that since this file is created during build-time, there is far less startup overhead at runtime compared to DI frameworks that scan all classes for annotations when the application is deployed.
The inhabitants file is used to populate a ServiceLocator when the application is started, from which services can be retrieved.
public static void main(String[] args) { ServiceLocator serviceLocator = ServiceLocatorUtilities.createAndPopulateServiceLocator(); Foo foo = serviceLocator.getService(Foo.class); foo.stuffDone(); }
This will print out:
postConstruct of class Bar A string returned from class Bar
A Programmatic Example
HK2 can also have classes added programmatically. So instead of using the maven plugin, it could be done with a main method like this:
public static void main(String[] args) { ServiceLocator serviceLocator = ServiceLocatorUtilities.createAndPopulateServiceLocator(); ServiceLocatorUtilities.addClasses(serviceLocator, Foo.class, Bar.class); Foo foo = serviceLocator.getService(Foo.class); Foo.stuffDone(); }
HK2 allows adding an instance of a class to the ServiceLocator as well. So we could change class Bar to inject something and print it out:
@Service public class Bar { @Inject @Named("doof") String food; public String doStuff() { System.out.printIn(food); return "A string returned from class Bar" ; } }
And that String has been initialised elsewhere, in the main method for this example:
public static void main(String[] args) { ServiceLocator serviceLocator = ServiceLocatorUtilities.createAndPopulateServiceLocator(); ServiceLocatorUtilities.addClasses(serviceLocator, Foo.class, Bar.class); Foo foo = serviceLocator.getService(Foo.class); foo.stuffDone() ; }
This will print out:
postConstruct of class Bar An injected string A string returned from class Bar
HK2 has many other features, including an event system. It has support for the JSR 330 scope Singleton (one instance of service across application), and has its own built in scopes of PerThread (matches the lifecycle of the thread it's on) and PerLookup (a new instance is created each time it is injected), with the capabilities of creating custom scopes. It includes RunLevel, which similar to that in UNIX can be used to start groups of services gradually; as well as interceptors, bean configuration and a limited form of bean validation.
For further information on HK2 see the documentation and Javadoc.
HK2 vs CDI
HK2 and CDI are very similar, as HK2 is a DI framework. The difference, as you may notice from the acronyms, is that CDI stands for Context and Dependency Injection, and as such has the built-in scopes of @ApplicationScoped, @ConversationScoped, @RequestScoped and @SessionScoped which HK2 does not have by default, although HK2 does support @Singleton which is almost identical to Application Scope. Other scopes can be created with HK2. The benefit of HK2 is that it is smaller is size and faster compared to Weld (the reference implementation of CDI). A specific example of that is the build-time generated inhabitants file, which saves on startup time. Obviously this is at the cost of build time, but often an application is started more often than its build.
The Future
As one of the crucial components of Payara Server, Payara will help maintain HK2 under the Eclipse Foundation as part of Jakarta EE. We are anticipating a 2.6.0 release, which will include various minor improvements, such as making ServiceLocator work with try-with-resources, and also getting it running on JDK11+.
In the further future we are looking to turn HK2 into a full CDI implementation as an alternative to Weld, which would allow us to have one dependency injection framework in Payara Platform to manage all services, for HK2 3.0.
Related Posts
Nugget Friday - Preventing Memory Leaks with JDBC Connection Pool Management in Payara Server
Published on 15 Nov 2024
by Luqman Saeed
0 Comments
What is an Application Server?
Published on 04 Nov 2024
by Dawn Baird
0 Comments