1. Introduction
Welcome Micronaut: a modern JVM-based full-stack framework. If you have not yet heard of Micronaut, then it is time to get informed and try it out. In this blog post you will get acquainted with Micronaut’s key features, scaffolding tools and a simple HTTP server and client example.
Here is a list of Micronaut’s key features:
Supports Java, Groovy, and Kotlin. Being a JVM-based framework, Micronaut provides first-class support for Java, Groovy and Kotlin.
Natively cloud-native. Micronaut’s cloud support is built right in, including support for common discovery services, distributed tracing tools, and cloud runtimes. Micronaut is ready to develop serverless applications and is designed for building resilient microservices.
Fast startup time and low memory consumption. Micronaut avoids using reflection at runtime and uses compile-time AST transformations instead. This is why Micronaut has a fast startup time and a minimal memory footprint.
Reactive and non-blocking. Micronaut is a non-blocking HTTP server built on Netty and has a declarative, reactive and compile-time HTTP client.
Fast and easy testing. Efficient compile-time dependency injection and AOP.
Fast data-access configuration. Micronaut provides sensible defaults that automatically configure your favourite data access toolkit and APIs to make it easy to write your own integrations.
2. Prerequisites
In order to run the examples in this article, you need to install the following tools:
SDKMAN – a tool for managing parallel versions of multiple SDKs
curl -s "https://get.sdkman.io" | bash
Java Development Kit
sdk install java
Micronaut CLI
sdk install micronaut
3. Scaffolding
As many other modern frameworks, Micronaut comes with a handy CLI scaffolding tool. Micronaut CLI can be used as a CLI tool:
1mn help 2mn help create-app
It can also be used in interactive mode with command completion.
1$ mn 2| Starting interactive mode... 3| Enter a command name to run. Use TAB for completion: 4mn> help 5... 6mn> help create-app 7...
Try both modes and see what you like more.
3.1. Creating a new Gradle project
Micronaut supports Java, Groovy, and Kotlin as first-class citizens. Let us create a new Java application using Micronaut CLI:
1mn create-app mn-hello-java
This will scaffold a new Gradle project. If you prefer Maven, add a --build maven
parameter. If you want to create a new Groovy or Kotlin project, add a --lang
parameter:
1mn create-app --lang groovy mn-hello-groovy 2mn create-app --lang kotlin mn-hello-kotlin
By default Micronaut HTTP server will listen on a random port, but you can alter that by adding the following configuration to src/main/resources/application.yml:
1micronaut: 2 server: 3 port: 8080
The application can be immediately run with Gradle:
1cd mn-hello-java 2./gradlew run 3> Task :run 4 511:56:49.340 [main] INFO io.micronaut.runtime.Micronaut - Startup completed in 654ms. Server Running: http://localhost:8080 6<=========----> 75% EXECUTING [7s]
Now we can check that Micronaut is running:
1curl http://localhost:8080 2{"_links":{"self":{"href":"/","templated":false}},"message":"Page Not Found"}
To run the application with IntelliJ IDEA, you need to enable annotation processing:
open Settings / Build, Execution, Deployment / Compiler / Annotation Processors
Set the checkbox “Enable annotation processing”
Then you can run the application by executing the application class in IDEA.
3.2. Adding an HTTP controller
Let us use the CLI again to create an HTTP controller. Run this command in the directory of the project that we created:
1mn create-controller HelloController 2| Rendered template Controller.java to destination src/main/java/mn/hello/java/HelloController.java 3| Rendered template ControllerTest.java to destination src/test/java/mn/hello/java/HelloControllerTest.java
As we can see, Micronaut cares about Test-Driven Development and has created a test along with the controller:
1package mn.hello.java;
2
3import io.micronaut.http.annotation.Controller;
4import io.micronaut.http.annotation.Get;
5import io.micronaut.http.HttpStatus;
6
7@Controller("/hello")
8public class HelloController {
9
10 @Get("/")
11 public HttpStatus index() {
12 return HttpStatus.OK;
13 }
14}
1package mn.hello.java;
2
3import io.micronaut.context.ApplicationContext;
4import io.micronaut.http.HttpStatus;
5import io.micronaut.http.client.RxHttpClient;
6import io.micronaut.runtime.server.EmbeddedServer;
7import org.junit.Test;
8
9
10import static org.junit.Assert.assertEquals;
11
12public class HelloControllerTest {
13
14 @Test
15 public void testIndex() throws Exception {
16 EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class);
17
18 RxHttpClient client = server.getApplicationContext().createBean(RxHttpClient.class, server.getURL());
19
20 assertEquals(HttpStatus.OK, client.toBlocking().exchange("/hello").status());
21 server.stop();
22 }
23}
3.3. Adding a compile-time-generated HTTP client
Micronaut provides a great declarative compile-time HTTP client. You can use it for testing your own server (as we will do in the next example) or for communicating with external servers.
1mn create-client HelloClient 2| Rendered template Client.java to destination src/main/java/mn/hello/java/HelloClient.java
Here is what Micronaut creates:
1package mn.hello.java;
2
3import io.micronaut.http.client.Client;
4import io.micronaut.http.annotation.Get;
5import io.micronaut.http.HttpStatus;
6
7@Client("hello")
8public interface HelloClient {
9
10 @Get("/")
11 HttpStatus index();
12}
That is all of it – only the interface. Micronaut will generate the implementation at compile time.
4. Hello World
Now that we got acquainted with Micronaut’s scaffolding CLI, let us create a more comprehensive example. We will extract the server API and reuse it in the HTTP controller in the production code and in the HTTP client in the test:
4.1. Server API
First, let us define the server API:
1package mn.hello.java;
2
3import io.micronaut.http.annotation.Get;
4import io.micronaut.http.annotation.QueryValue;
5
6public interface HelloApi {
7
8 @Get("/")
9 HelloMessage index(@QueryValue("name") String name);
10}
Here is what is happening:
the annotation
@Get("/")
specifies the path and the HTTP method of the endpointthe annotation
@QueryValue("name")
maps the GET parametername
to the method parametername
the return type
HelloMessage
tells Micronaut to serialize the POJO returned by the HTTP controller to JSON
1package mn.hello.java;
2
3public class HelloMessage {
4 public String greeting;
5}
4.2. HTTP controller
Let us implement a simple HTTP controller that will return a greeting for the given name:
1package mn.hello.java;
2
3import io.micronaut.http.annotation.Controller;
4
5@Controller("/hello")
6public class HelloController implements HelloApi {
7
8 @Override
9 public HelloMessage index(String name) {
10 HelloMessage m = new HelloMessage();
11 m.greeting = "Hello " + name + "!";
12 return m;
13 }
14}
Since we return a POJO in the controller, Micronaut considers the method blocking and will execute it on the I/O thread pool. However, it is also possible to write a non-blocking reactive implementation and return a non-blocking type such as Single
. In this case, the request is considered non-blocking and the method will be executed on the Netty event loop thread.
4.3. HTTP client & testing
Since we extracted the server API into a separate interface, we can now easily create an HTTP client for our application:
1package mn.hello.java;
2
3import io.micronaut.http.client.Client;
4
5@Client("/hello")
6public interface HelloClient extends HelloApi {}
We don’t need to write any implementation, Micronaut will do that for us at compile time. Here is what a test can look like using this HTTP client:
1package mn.hello.java;
2
3import io.micronaut.context.ApplicationContext;
4import io.micronaut.runtime.server.EmbeddedServer;
5import org.junit.Test;
6
7import static org.junit.Assert.assertEquals;
8
9public class HelloControllerTest {
10
11 @Test
12 public void testIndex() throws Exception {
13 EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class);
14
15 HelloClient client = server.getApplicationContext().getBean(HelloClient.class);
16
17 HelloMessage serverResponse = client.index("codecentric");
18
19 assertEquals("Hello codecentric!", serverResponse.greeting);
20 server.stop();
21 }
22}
In this test we actually spin up our Micronaut application and execute an HTTP request against it using the generated HTTP client.
4.4. Docker-ready
Micronaut CLI generates a Dockerfile as well, making it easy to package your application for a container environment such as Kubernetes. Let us run our example application with Docker:
Build the application into a fat-jar:
./gradlew build
Build a Docker image:
docker build . -t mn-hello-world
Run the Docker image:
docker run --rm -p 8080:8080 mn-hello-world
Check that it is running:
curl http://localhost:8080/hello?name=codecentric {"greeting":"Hello codecentric!"}
5. Conclusion
In this article we have only scratched the surface of what Micronaut has to offer. Here is what’s left out of scope:
Reactive programming
Database access with GORM
Service discovery
Serverless applications
Distributed tracing
All in all, Micronaut is a fast-evolving framework and looks very promising. Go try it yourself or maybe even use it for your next project!
6. Links
Micronaut home page: http://micronaut.io/
Your job at codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
More articles in this subject area
Discover exciting further topics and let the codecentric world inspire you.
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog author
Andrey Vetlugin
IT Consultant & Developer
Do you still have questions? Just send me a message.
Do you still have questions? Just send me a message.