Standalone Micronaut Application
So you want to have dependency injection in your new Java playground, but donβt want to mess with XML from Spring Context? In this article youβll find out how! π
A Sneak Peak of the End
After following this guide, youβll have a simple Java application, with compile-time generated dependency injection capabilities, along with a few more nice features from Micronaut, like the Configuration Properties and the AOP.
If you just want to see the final result, here is the repository:
So, without further ado, letβs dive into the tutorial:
1. Generating the project
Iβll use Gradle as my build tool so we can take advantage of the new Micronaut Gradle Plugin. And before I forget, Iβll be using JDK version 15, but I think anything above 11 will do.
So begin by first creating a new folder and make sure to open a terminal inside it:
mkdir awesome-micronaut-app && cd awesome-micronaut-app
After that, we can initialize an empty Gradle project
gradle init
We want an application, so make sure to answer 2: application
when asked by the process. Following the questions, we want a Java app, so 3: Java
. For the sake of simplicity, Iβll stick with a single application, without subprojects, so for the next question, the answer should be: 1: no, only one application project
. Groovy Build Script, JUnit Jupiter as the test framework, and lastly choose a name for your app.
After the project is created, youβll probably have a folder structure like:
βββ app
β βββ build.gradle
β βββ src
β βββ main
β β βββ java
β β β βββ com
β β β βββ awesome
β β β βββ micronaut
β β β βββ App.java
β β βββ resources
β βββ test
β βββ java
β β βββ com
β β βββ awesome
β β βββ micronaut
β β βββ AppTest.java
β βββ resources
βββ gradle
β βββ wrapper
β βββ gradle-wrapper.jar
β βββ gradle-wrapper.properties
βββ gradlew
βββ gradlew.bat
βββ settings.gradle
2. Configuring Micronaut
To be able to use the awesome Micronaut features we need to import then in some way, so letβs get that done!
Before touching anything, we need to open the project in a text editor or IDE, you can use IntelliJ, VSCode, or any other IDE/text editor youβre familiar with.
So after youβve opened the folder in your favorite IDE, we need to change a few things in the app/build.gradle
. Check out the full file below, paying attention to the changes in bold (comments removed for brevity):
plugins {
id 'application'
}ext {
micronautVersion = '2.3.0'
}repositories {
jcenter()
mavenCentral();
}dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
implementation 'com.google.guava:guava:29.0-jre'
// Micronaut Dependencies
annotationProcessor(platform("io.micronaut:micronaut-bom:${micronautVersion}"))
annotationProcessor("io.micronaut:micronaut-inject-java")
implementation(platform("io.micronaut:micronaut-bom:${micronautVersion}"))
implementation("io.micronaut:micronaut-inject")
implementation("io.micronaut:micronaut-aop")
testAnnotationProcessor(platform("io.micronaut:micronaut-bom:${micronautVersion}"))
testAnnotationProcessor("io.micronaut:micronaut-inject-java") // To remove SLF4J warnings
runtimeOnly "org.slf4j:slf4j-simple"
}application {
mainClass = 'com.awesome.micronaut.App'
}java {
sourceCompatibility = JavaVersion.VERSION_15
targetCompatibility = JavaVersion.VERSION_15
}tasks.named('test') {
useJUnitPlatform()
}
Remember to change the packages according to the name of your app. If something does not work for you, check out the full diff here.
After that, go to the terminal and install the dependencies and build the application (or just refresh Gradle in IntelliJ). Also the generated test class for the App class (AppTest.java) might break, so be sure to fix that before proceeding.
./gradlew clean build
And thatβs it, you have a full blown Java application, with Dependency Injection, YML configuration, Application Event Listener/Publisher and much more!
If you know your way through Micronaut and how all this stuff works, youβre done, otherwise letβs see how we can put all this together in the following section.
3. Testing the available features
So, in this section you will see some of the things that are already working for you without out of the box.
3.1. Configuration Properties
Letβs create a very simple configuration properties binding, so we can use an application.yml file to store our app config.
import io.micronaut.context.annotation.ConfigurationProperties;@ConfigurationProperties("some.configuration")
public class SomeConfiguration { private String value; public String getValue() {
return this.value;
} public void setValue(String value) {
this.value = value;
}
}
And also, lets create the configuration file at: src/main/resources/application.yml
some:
configuration:
value: "this tutorial is awesome!"
We will see it working in a few moments, letβs first add a few more features.
3.2. Application Events
Events are very popular nowadays, as a way to decouple the code. And is also very easy to implement with Micronaut.
We need three parts: an event β which will be published by someone; an event listener β who will react to specific events; and an event publisher β who will dispatch our events to the right listeners. But we only need to implement the event listener and the event itself, because Micronaut takes care of the publisher for us.
Both are very simple so letβs take a look at the implementation:
import io.micronaut.context.event.ApplicationEvent;public class SomeEvent extends ApplicationEvent { public SomeEvent(Object source) {
super(source);
}
}
The event listener also has the @Singleton
annotation so that the Micronaut runtime can find it and register it into the ApplicationContext
.
import io.micronaut.context.event.ApplicationEventListener;import javax.inject.Singleton;@Singleton
public class SomeEventListener implements ApplicationEventListener<SomeEvent> { @Override
public boolean supports(SomeEvent event) {
return true;
} @Override
public void onApplicationEvent(SomeEvent event) {
System.out.println("Received an event!");
}
}
3.3. Dependency Injection
To wire it all together, letβs create a simple class and inject our configuration and the event publisher.
import io.micronaut.context.event.ApplicationEventPublisher;import javax.inject.Singleton;
import javax.inject.Inject;@Singleton
public class SomeDependency { @Inject
private SomeConfiguration someConfiguration; @Inject
private ApplicationEventPublisher applicationEventPublisher; public void doStuff() {
System.out.println("doing some stuff...");
} public void publishEvent() {
System.out.println("publishing event...");
applicationEventPublisher.publishEvent(new SomeEvent(this));
} public void consumeConfiguration() {
System.out.println("The configuration value is: " + someConfiguration.getValue());
}}
3.4. The ApplicationContext
This is the last part, but is the most important one, because here we actually initialize Micronaut ApplicationContext, and it is where most of the magic happens (the other part being the annotation processing powers).
public static void main(String[] args) {
ApplicationContext applicationContext = ApplicationContext.run(); var dependency = applicationContext.getBean(SomeDependency.class); dependency.doStuff(); dependency.publishEvent(); dependency.consumeConfiguration();
}
Wew! That was a lot of code, itβs finally time to see some action!
So, if you followed along, and imported all the correct classes, when you run the βrunβ Gradle task, you should see something like this:
> Task :app:run
doing some stuff...
publishing event...
Received an event!
The configuration value is: this tutorial is awesome!
And if you got any problems, check out the full code on the repo below:
4. Conclusion
I made this tutorial because I wanted to do some experiments with Micronaut without the Web Server part, and found it very difficult to find out the directions, so I hope this helps someone with the same problem.
Micronaut is a very interesting framework because it minimizes the use of reflection and instead use annotation processing to generate metadata at compile-time, making it very fast! Iβll probably write some more tutorials on the topic later down the road.
This article was my first full blown tutorial with code, so if you have any tips, feel free to comment, and if I wasnβt clear enough let me now.
Thank you very much for your time!