A Micronaut introduction

Micronaut Discovery (3 Part Series)

1 A Micronaut introduction
2 CLI applications with Micronaut and Picocli
3 Native images with Micronaut and GraalVM

Note: this article was originally published on Stack Labs blog.

Until not too long ago, when we wanted to develop JVM microservices, there wasn’t much choice beyond the almighty Spring Boot. This is not true anymore with the arrival of frameworks such as Eclipse Microprofile, Helidon, Quarkus or Micronaut. In this article we will focus on Micronaut framework to discover how we can use it to develop microservices through a simple hello-world app example.

Micronaut goal

You might be wondering why using another JVM framework when Spring Boot is already working quite well for us.

If we read Micronaut official documentation introduction, it becomes clear what Micronaut claims to provide:

  • Fast startup time
  • Reduced memory footprint
  • Minimal use of reflection
  • Minimal use of proxies
  • Easy unit testing

By providing these points Micronaut clearly takes a completely different approach to the one taken by older frameworks like Spring Boot, Spring or Grails, and Micronaut developers know what they are talking about because they are… former Grails framework developers !

Although there are downsides to famous historical frameworks, a lot of things work well (well, that’s why frameworks like Spring Boot became so popular, isn’t it ?) and Micronaut intends to keep all these well-working things, such as dependency injection, auto-configuration, service discovery and HTTP clients and servers.

Now that we know what Micronaut goal is, let’s dive into a real Micronaut application to see concretely how it goes for us developers ⌨️️

Creating a fully-featured app skeleton with Micronaut CLI

All the great frameworks provide a quick way to create a pre-configured app skeleton to enable for a very quick start. The idea behind this being that you spend less time initializing and configuring your app and more time actually coding and adding business value.

If you’ve ever seen one of Pivotal best speaker Josh Long, you already know “start dot spring dot io” where you can select the various features and other configurations for your brand new app.

Well Micronaut does not provide a web UI but a simple CLI called mn to allow easy app creation. You can easily install it using Sdkman or directly with Homebrew on a Mac.

Mac install:

> brew update && brew install micronaut
> brew update && brew install micronaut
> brew update && brew install micronaut

Enter fullscreen mode Exit fullscreen mode

Unix install:

# With Sdkman installed
> sdk update && sdk install micronaut
# With Sdkman installed
> sdk update && sdk install micronaut
# With Sdkman installed > sdk update && sdk install micronaut

Enter fullscreen mode Exit fullscreen mode

️ Let’s see what features Micronaut provides by running the following command:

> mn profile-info service
Resolving dependencies..
| Profile: service
--------------------
The service profile
| Provided Commands:
--------------------
create-bean Creates a singleton bean
create-client Creates a client interface
create-controller Creates a controller and associated test
create-job Creates a job with scheduled method
create-repository Creates a repository and associated test
create-test Creates a simple test for the project's testing framework
create-websocket-client Creates a Websocket client
create-websocket-server Creates a Websocket server
help Prints help information for a specific command
| Provided Features:
| (+) denotes features included by default.
--------------------
annotation-api (+) Adds Java annotation API
application (+) Facilitates creating an executable JVM application and adds support for creating fat/uber JARs
asciidoctor Adds Asciidoctor documentation
aws-api-gateway Adds support for AWS API Gateway
aws-api-gateway-graal Creates an AWS API Gateway Proxy Lambda with Graal Native Image
cassandra Adds support for Cassandra in the application
config-consul Adds support for Distributed Configuration with Consul (https://www.consul.io)
data-hibernate-jpa Adds support for Micronaut Data Hibernate/JPA
data-jdbc Adds support for Micronaut Data JDBC
discovery-consul Adds support for Service Discovery with Consul (https://www.consul.io)
discovery-eureka Adds support for Service Discovery with Eureka
ehcache Adds support for Ehcache (https://www.ehcache.org/)
elasticsearch Adds support for Elasticsearch in the application
file-watch Adds automatic restarts and file watch
flyway Adds support for Flyway database migrations (https://flywaydb.org/)
graal-native-image Allows Building a GraalVM Native Image
graphql Adds support for GraphQL in the application
groovy Creates a Groovy application
hazelcast Adds support for Hazelcast (https://hazelcast.org)
hibernate-gorm Adds support for GORM persistence framework
hibernate-jpa Adds support for Hibernate/JPA
http-client (+) Adds support for creating HTTP clients
http-server (+) Adds support for running a Netty server
java Creates a Java application
jdbc-dbcp Configures SQL DataSource instances using Commons DBCP
jdbc-hikari Configures SQL DataSource instances using Hikari Connection Pool
jdbc-tomcat Configures SQL DataSource instances using Tomcat Connection Pool
jib Adds support for Jib builds
jrebel Adds support for class reloading with JRebel (requires separate JRebel installation)
junit Adds support for the JUnit 5 testing framework
kafka Adds support for Kafka
kafka-streams Adds support for Kafka Streams
kotlin Creates a Kotlin application
kotlintest Adds support for the KotlinTest testing framework
kubernetes Adds support for Kubernetes
liquibase Adds support for Liquibase database migrations (http://www.liquibase.org/)
log4j2 Adds Log4j2 Logging
logback (+) Adds Logback Logging
management Adds support for management endpoints
micrometer Adds support for Micrometer metrics
micrometer-appoptics Adds support for Micrometer metrics (w/ AppOptics reporter)
micrometer-atlas Adds support for Micrometer metrics (w/ Atlas reporter)
micrometer-azure-monitor Adds support for Micrometer metrics (w/ Azure Monitor reporter)
micrometer-cloudwatch Adds support for Micrometer metrics (w/ AWS Cloudwatch reporter)
micrometer-datadog Adds support for Micrometer metrics (w/ Datadog reporter)
micrometer-dynatrace Adds support for Micrometer metrics (w/ Dynatrace reporter)
micrometer-elastic Adds support for Micrometer metrics (w/ Elastic reporter)
micrometer-ganglia Adds support for Micrometer metrics (w/ Ganglia reporter)
micrometer-graphite Adds support for Micrometer metrics (w/ Graphite reporter)
micrometer-humio Adds support for Micrometer metrics (w/ Humio reporter)
micrometer-influx Adds support for Micrometer metrics (w/ Influx reporter)
micrometer-jmx Adds support for Micrometer metrics (w/ Jmx reporter)
micrometer-kairos Adds support for Micrometer metrics (w/ Kairos reporter)
micrometer-new-relic Adds support for Micrometer metrics (w/ New Relic reporter)
micrometer-prometheus Adds support for Micrometer metrics (w/ Prometheus reporter)
micrometer-signalfx Adds support for Micrometer metrics (w/ SignalFx reporter)
micrometer-stackdriver Adds support for Micrometer metrics (w/ Stackdriver reporter)
micrometer-statsd Adds support for Micrometer metrics (w/ Statsd reporter)
micrometer-wavefront Adds support for Micrometer metrics (w/ Wavefront reporter)
mongo-gorm Configures GORM for MongoDB for Groovy applications
mongo-reactive Adds support for the Mongo Reactive Streams Driver
neo4j-bolt Adds support for the Neo4j Bolt Driver
neo4j-gorm Configures GORM for Neo4j for Groovy applications
netflix-archaius Adds support for Netflix Archaius in the application
netflix-hystrix Adds support for Netflix Hystrix in the application
netflix-ribbon Adds support for Netflix Ribbon in the application
picocli Adds support for command line parsing (http://picocli.info)
postgres-reactive Adds support for the Reactive Postgres driver in the application
rabbitmq Adds support for RabbitMQ in the application
redis-lettuce Configures the Lettuce driver for Redis
security-jwt Adds support for JWT (JSON Web Token) based Authentication
security-session Adds support for Session based Authentication
spek Adds support for the Spek testing framework
spock Adds support for the Spock testing framework
springloaded Adds support for class reloading with Spring-Loaded
swagger-groovy Configures Swagger (OpenAPI) Integration for Groovy
swagger-java Configures Swagger (OpenAPI) Integration for Java
swagger-kotlin Configures Swagger (OpenAPI) Integration for Kotlin
tracing-jaeger Adds support for distributed tracing with Jaeger (https://www.jaegertracing.io)
tracing-zipkin Adds support for distributed tracing with Zipkin (https://zipkin.io)
vertx-mysql-client Add support for the Reactive MySQL Client in the application
vertx-pg-client Add support for the Reactive Postgres Client in the application
> mn profile-info service

Resolving dependencies..
| Profile: service
--------------------
The service profile

| Provided Commands:
--------------------
  create-bean              Creates a singleton bean
  create-client            Creates a client interface
  create-controller        Creates a controller and associated test
  create-job               Creates a job with scheduled method
  create-repository        Creates a repository and associated test
  create-test              Creates a simple test for the project's testing framework
  create-websocket-client  Creates a Websocket client
  create-websocket-server  Creates a Websocket server
  help                     Prints help information for a specific command

| Provided Features:
| (+) denotes features included by default.
--------------------
  annotation-api (+)        Adds Java annotation API
  application (+)           Facilitates creating an executable JVM application and adds support for creating fat/uber JARs
  asciidoctor               Adds Asciidoctor documentation
  aws-api-gateway           Adds support for AWS API Gateway
  aws-api-gateway-graal     Creates an AWS API Gateway Proxy Lambda with Graal Native Image
  cassandra                 Adds support for Cassandra in the application
  config-consul             Adds support for Distributed Configuration with Consul (https://www.consul.io)
  data-hibernate-jpa        Adds support for Micronaut Data Hibernate/JPA
  data-jdbc                 Adds support for Micronaut Data JDBC
  discovery-consul          Adds support for Service Discovery with Consul (https://www.consul.io)
  discovery-eureka          Adds support for Service Discovery with Eureka
  ehcache                   Adds support for Ehcache (https://www.ehcache.org/)
  elasticsearch             Adds support for Elasticsearch in the application
  file-watch                Adds automatic restarts and file watch
  flyway                    Adds support for Flyway database migrations (https://flywaydb.org/)
  graal-native-image        Allows Building a GraalVM Native Image
  graphql                   Adds support for GraphQL in the application
  groovy                    Creates a Groovy application
  hazelcast                 Adds support for Hazelcast (https://hazelcast.org)
  hibernate-gorm            Adds support for GORM persistence framework
  hibernate-jpa             Adds support for Hibernate/JPA
  http-client (+)           Adds support for creating HTTP clients
  http-server (+)           Adds support for running a Netty server
  java                      Creates a Java application
  jdbc-dbcp                 Configures SQL DataSource instances using Commons DBCP
  jdbc-hikari               Configures SQL DataSource instances using Hikari Connection Pool
  jdbc-tomcat               Configures SQL DataSource instances using Tomcat Connection Pool
  jib                       Adds support for Jib builds
  jrebel                    Adds support for class reloading with JRebel (requires separate JRebel installation)
  junit                     Adds support for the JUnit 5 testing framework
  kafka                     Adds support for Kafka
  kafka-streams             Adds support for Kafka Streams
  kotlin                    Creates a Kotlin application
  kotlintest                Adds support for the KotlinTest testing framework
  kubernetes                Adds support for Kubernetes
  liquibase                 Adds support for Liquibase database migrations (http://www.liquibase.org/)
  log4j2                    Adds Log4j2 Logging
  logback (+)               Adds Logback Logging
  management                Adds support for management endpoints
  micrometer                Adds support for Micrometer metrics
  micrometer-appoptics      Adds support for Micrometer metrics (w/ AppOptics reporter)
  micrometer-atlas          Adds support for Micrometer metrics (w/ Atlas reporter)
  micrometer-azure-monitor  Adds support for Micrometer metrics (w/ Azure Monitor reporter)
  micrometer-cloudwatch     Adds support for Micrometer metrics (w/ AWS Cloudwatch reporter)
  micrometer-datadog        Adds support for Micrometer metrics (w/ Datadog reporter)
  micrometer-dynatrace      Adds support for Micrometer metrics (w/ Dynatrace reporter)
  micrometer-elastic        Adds support for Micrometer metrics (w/ Elastic reporter)
  micrometer-ganglia        Adds support for Micrometer metrics (w/ Ganglia reporter)
  micrometer-graphite       Adds support for Micrometer metrics (w/ Graphite reporter)
  micrometer-humio          Adds support for Micrometer metrics (w/ Humio reporter)
  micrometer-influx         Adds support for Micrometer metrics (w/ Influx reporter)
  micrometer-jmx            Adds support for Micrometer metrics (w/ Jmx reporter)
  micrometer-kairos         Adds support for Micrometer metrics (w/ Kairos reporter)
  micrometer-new-relic      Adds support for Micrometer metrics (w/ New Relic reporter)
  micrometer-prometheus     Adds support for Micrometer metrics (w/ Prometheus reporter)
  micrometer-signalfx       Adds support for Micrometer metrics (w/ SignalFx reporter)
  micrometer-stackdriver    Adds support for Micrometer metrics (w/ Stackdriver reporter)
  micrometer-statsd         Adds support for Micrometer metrics (w/ Statsd reporter)
  micrometer-wavefront      Adds support for Micrometer metrics (w/ Wavefront reporter)
  mongo-gorm                Configures GORM for MongoDB for Groovy applications
  mongo-reactive            Adds support for the Mongo Reactive Streams Driver
  neo4j-bolt                Adds support for the Neo4j Bolt Driver
  neo4j-gorm                Configures GORM for Neo4j for Groovy applications
  netflix-archaius          Adds support for Netflix Archaius in the application
  netflix-hystrix           Adds support for Netflix Hystrix in the application
  netflix-ribbon            Adds support for Netflix Ribbon in the application
  picocli                   Adds support for command line parsing (http://picocli.info)
  postgres-reactive         Adds support for the Reactive Postgres driver in the application
  rabbitmq                  Adds support for RabbitMQ in the application
  redis-lettuce             Configures the Lettuce driver for Redis
  security-jwt              Adds support for JWT (JSON Web Token) based Authentication
  security-session          Adds support for Session based Authentication
  spek                      Adds support for the Spek testing framework
  spock                     Adds support for the Spock testing framework
  springloaded              Adds support for class reloading with Spring-Loaded
  swagger-groovy            Configures Swagger (OpenAPI) Integration for Groovy
  swagger-java              Configures Swagger (OpenAPI) Integration for Java
  swagger-kotlin            Configures Swagger (OpenAPI) Integration for Kotlin
  tracing-jaeger            Adds support for distributed tracing with Jaeger (https://www.jaegertracing.io)
  tracing-zipkin            Adds support for distributed tracing with Zipkin (https://zipkin.io)
  vertx-mysql-client        Add support for the Reactive MySQL Client in the application
  vertx-pg-client           Add support for the Reactive Postgres Client in the application
> mn profile-info service Resolving dependencies.. | Profile: service -------------------- The service profile | Provided Commands: -------------------- create-bean Creates a singleton bean create-client Creates a client interface create-controller Creates a controller and associated test create-job Creates a job with scheduled method create-repository Creates a repository and associated test create-test Creates a simple test for the project's testing framework create-websocket-client Creates a Websocket client create-websocket-server Creates a Websocket server help Prints help information for a specific command | Provided Features: | (+) denotes features included by default. -------------------- annotation-api (+) Adds Java annotation API application (+) Facilitates creating an executable JVM application and adds support for creating fat/uber JARs asciidoctor Adds Asciidoctor documentation aws-api-gateway Adds support for AWS API Gateway aws-api-gateway-graal Creates an AWS API Gateway Proxy Lambda with Graal Native Image cassandra Adds support for Cassandra in the application config-consul Adds support for Distributed Configuration with Consul (https://www.consul.io) data-hibernate-jpa Adds support for Micronaut Data Hibernate/JPA data-jdbc Adds support for Micronaut Data JDBC discovery-consul Adds support for Service Discovery with Consul (https://www.consul.io) discovery-eureka Adds support for Service Discovery with Eureka ehcache Adds support for Ehcache (https://www.ehcache.org/) elasticsearch Adds support for Elasticsearch in the application file-watch Adds automatic restarts and file watch flyway Adds support for Flyway database migrations (https://flywaydb.org/) graal-native-image Allows Building a GraalVM Native Image graphql Adds support for GraphQL in the application groovy Creates a Groovy application hazelcast Adds support for Hazelcast (https://hazelcast.org) hibernate-gorm Adds support for GORM persistence framework hibernate-jpa Adds support for Hibernate/JPA http-client (+) Adds support for creating HTTP clients http-server (+) Adds support for running a Netty server java Creates a Java application jdbc-dbcp Configures SQL DataSource instances using Commons DBCP jdbc-hikari Configures SQL DataSource instances using Hikari Connection Pool jdbc-tomcat Configures SQL DataSource instances using Tomcat Connection Pool jib Adds support for Jib builds jrebel Adds support for class reloading with JRebel (requires separate JRebel installation) junit Adds support for the JUnit 5 testing framework kafka Adds support for Kafka kafka-streams Adds support for Kafka Streams kotlin Creates a Kotlin application kotlintest Adds support for the KotlinTest testing framework kubernetes Adds support for Kubernetes liquibase Adds support for Liquibase database migrations (http://www.liquibase.org/) log4j2 Adds Log4j2 Logging logback (+) Adds Logback Logging management Adds support for management endpoints micrometer Adds support for Micrometer metrics micrometer-appoptics Adds support for Micrometer metrics (w/ AppOptics reporter) micrometer-atlas Adds support for Micrometer metrics (w/ Atlas reporter) micrometer-azure-monitor Adds support for Micrometer metrics (w/ Azure Monitor reporter) micrometer-cloudwatch Adds support for Micrometer metrics (w/ AWS Cloudwatch reporter) micrometer-datadog Adds support for Micrometer metrics (w/ Datadog reporter) micrometer-dynatrace Adds support for Micrometer metrics (w/ Dynatrace reporter) micrometer-elastic Adds support for Micrometer metrics (w/ Elastic reporter) micrometer-ganglia Adds support for Micrometer metrics (w/ Ganglia reporter) micrometer-graphite Adds support for Micrometer metrics (w/ Graphite reporter) micrometer-humio Adds support for Micrometer metrics (w/ Humio reporter) micrometer-influx Adds support for Micrometer metrics (w/ Influx reporter) micrometer-jmx Adds support for Micrometer metrics (w/ Jmx reporter) micrometer-kairos Adds support for Micrometer metrics (w/ Kairos reporter) micrometer-new-relic Adds support for Micrometer metrics (w/ New Relic reporter) micrometer-prometheus Adds support for Micrometer metrics (w/ Prometheus reporter) micrometer-signalfx Adds support for Micrometer metrics (w/ SignalFx reporter) micrometer-stackdriver Adds support for Micrometer metrics (w/ Stackdriver reporter) micrometer-statsd Adds support for Micrometer metrics (w/ Statsd reporter) micrometer-wavefront Adds support for Micrometer metrics (w/ Wavefront reporter) mongo-gorm Configures GORM for MongoDB for Groovy applications mongo-reactive Adds support for the Mongo Reactive Streams Driver neo4j-bolt Adds support for the Neo4j Bolt Driver neo4j-gorm Configures GORM for Neo4j for Groovy applications netflix-archaius Adds support for Netflix Archaius in the application netflix-hystrix Adds support for Netflix Hystrix in the application netflix-ribbon Adds support for Netflix Ribbon in the application picocli Adds support for command line parsing (http://picocli.info) postgres-reactive Adds support for the Reactive Postgres driver in the application rabbitmq Adds support for RabbitMQ in the application redis-lettuce Configures the Lettuce driver for Redis security-jwt Adds support for JWT (JSON Web Token) based Authentication security-session Adds support for Session based Authentication spek Adds support for the Spek testing framework spock Adds support for the Spock testing framework springloaded Adds support for class reloading with Spring-Loaded swagger-groovy Configures Swagger (OpenAPI) Integration for Groovy swagger-java Configures Swagger (OpenAPI) Integration for Java swagger-kotlin Configures Swagger (OpenAPI) Integration for Kotlin tracing-jaeger Adds support for distributed tracing with Jaeger (https://www.jaegertracing.io) tracing-zipkin Adds support for distributed tracing with Zipkin (https://zipkin.io) vertx-mysql-client Add support for the Reactive MySQL Client in the application vertx-pg-client Add support for the Reactive Postgres Client in the application

Enter fullscreen mode Exit fullscreen mode

Great, we can see all the features provided by Micronaut. By the way we can note that some features such as annotation-api, application, http-client, http-server or logback are included by default in any application created through mn command as they are considered basic features for any real-world application

️ Let’s now create a hello-world Java application with a few features (plus features included by default) and with Maven build tool (mn defaults to Gradle):

> mn create-app com.stacklabs.micronaut.hello-world --lang=java --build=maven --features=junit
| Generating Java project...
| Application created at /Users/path/to/the/micronaut-workshop/hello-world
> mn create-app com.stacklabs.micronaut.hello-world --lang=java --build=maven --features=junit 
| Generating Java project...
| Application created at /Users/path/to/the/micronaut-workshop/hello-world
> mn create-app com.stacklabs.micronaut.hello-world --lang=java --build=maven --features=junit | Generating Java project... | Application created at /Users/path/to/the/micronaut-workshop/hello-world

Enter fullscreen mode Exit fullscreen mode

Great, we now have our app skeleton, time to code !

Note: Micronaut CLI is great for initializing an app and avoiding the creation of Maven/Gradle boilerplate, but it is pretty basic and you cannot use it to add new features to your app once it has been initialized. If you have forgotten features, you will have to add them manually on your existing app build.

Creating a simple controller

The controller

We will now create a controller, but before that take a look at the main class generated for us by mn command:

<span>public</span> <span>class</span> <span>Application</span> <span>{</span>
<span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>[]</span> <span>args</span><span>)</span> <span>{</span>
<span>Micronaut</span><span>.</span><span>run</span><span>(</span><span>Application</span><span>.</span><span>class</span><span>);</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>Application</span> <span>{</span>

    <span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>[]</span> <span>args</span><span>)</span> <span>{</span>
        <span>Micronaut</span><span>.</span><span>run</span><span>(</span><span>Application</span><span>.</span><span>class</span><span>);</span>
    <span>}</span>
<span>}</span>
public class Application { public static void main(String[] args) { Micronaut.run(Application.class); } }

Enter fullscreen mode Exit fullscreen mode

Not too complicated right ?

️ Now, to create our controller we can use mn command again:

> mn create-controller Hello
| Rendered template Controller.java to destination src/main/java/fr/stacklabs/HelloController.java
| Rendered template Spec.groovy to destination src/test/groovy/fr/stacklabs/HelloControllerSpec.groovy
> mn create-controller Hello
| Rendered template Controller.java to destination src/main/java/fr/stacklabs/HelloController.java
| Rendered template Spec.groovy to destination src/test/groovy/fr/stacklabs/HelloControllerSpec.groovy
> mn create-controller Hello | Rendered template Controller.java to destination src/main/java/fr/stacklabs/HelloController.java | Rendered template Spec.groovy to destination src/test/groovy/fr/stacklabs/HelloControllerSpec.groovy

Enter fullscreen mode Exit fullscreen mode

As we can see, the command line generated a Java class (because we created a Java app) for our controller and its test counterpart. Let’s have a look at this controller:

<span>import</span> <span>io.micronaut.http.annotation.Controller</span><span>;</span>
<span>import</span> <span>io.micronaut.http.annotation.Get</span><span>;</span>
<span>import</span> <span>io.micronaut.http.HttpStatus</span><span>;</span>
<span>@Controller</span><span>(</span><span>"/hello"</span><span>)</span>
<span>public</span> <span>class</span> <span>HelloController</span> <span>{</span>
<span>@Get</span><span>(</span><span>"/"</span><span>)</span>
<span>public</span> <span>HttpStatus</span> <span>index</span><span>()</span> <span>{</span>
<span>return</span> <span>HttpStatus</span><span>.</span><span>OK</span><span>;</span>
<span>}</span>
<span>}</span>
<span>import</span> <span>io.micronaut.http.annotation.Controller</span><span>;</span>
<span>import</span> <span>io.micronaut.http.annotation.Get</span><span>;</span>
<span>import</span> <span>io.micronaut.http.HttpStatus</span><span>;</span>

<span>@Controller</span><span>(</span><span>"/hello"</span><span>)</span>
<span>public</span> <span>class</span> <span>HelloController</span> <span>{</span>

    <span>@Get</span><span>(</span><span>"/"</span><span>)</span>
    <span>public</span> <span>HttpStatus</span> <span>index</span><span>()</span> <span>{</span>
        <span>return</span> <span>HttpStatus</span><span>.</span><span>OK</span><span>;</span>
    <span>}</span>
<span>}</span>
import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; import io.micronaut.http.HttpStatus; @Controller("/hello") public class HelloController { @Get("/") public HttpStatus index() { return HttpStatus.OK; } }

Enter fullscreen mode Exit fullscreen mode

Pretty basic, the default skeleton creates a controller with a simple endpoint returning a HTTP 200. Note that the @Controller and @Get annotations are not JAX-RS annotations but Micronaut specific annotations.

️ Let’s modify it so it adds “Hello Micronaut !” to our HTTP 200 response body:

<span>@Controller</span><span>(</span><span>"/hello"</span><span>)</span>
<span>public</span> <span>class</span> <span>HelloController</span> <span>{</span>
<span>@Get</span><span>(</span><span>"/"</span><span>)</span>
<span>public</span> <span>String</span> <span>index</span><span>()</span> <span>{</span>
<span>return</span> <span>"Hello Micronaut !"</span><span>;</span>
<span>}</span>
<span>}</span>
<span>@Controller</span><span>(</span><span>"/hello"</span><span>)</span>
<span>public</span> <span>class</span> <span>HelloController</span> <span>{</span>

    <span>@Get</span><span>(</span><span>"/"</span><span>)</span>
    <span>public</span> <span>String</span> <span>index</span><span>()</span> <span>{</span>
        <span>return</span> <span>"Hello Micronaut !"</span><span>;</span>
    <span>}</span>
<span>}</span>
@Controller("/hello") public class HelloController { @Get("/") public String index() { return "Hello Micronaut !"; } }

Enter fullscreen mode Exit fullscreen mode

The test

️ Let’s now open the test and check the default content:

<span>import</span> <span>io.micronaut.context.ApplicationContext</span><span>;</span>
<span>import</span> <span>io.micronaut.http.HttpStatus</span><span>;</span>
<span>import</span> <span>io.micronaut.http.client.RxHttpClient</span><span>;</span>
<span>import</span> <span>io.micronaut.runtime.server.EmbeddedServer</span><span>;</span>
<span>import</span> <span>io.micronaut.test.annotation.MicronautTest</span><span>;</span>
<span>import</span> <span>org.junit.jupiter.api.Test</span><span>;</span>
<span>import</span> <span>javax.inject.Inject</span><span>;</span>
<span>import</span> <span>static</span> <span>org</span><span>.</span><span>junit</span><span>.</span><span>jupiter</span><span>.</span><span>api</span><span>.</span><span>Assertions</span><span>.</span><span>assertEquals</span><span>;</span>
<span>@MicronautTest</span>
<span>public</span> <span>class</span> <span>HelloControllerTest</span> <span>{</span>
<span>@Inject</span>
<span>EmbeddedServer</span> <span>embeddedServer</span><span>;</span>
<span>@Test</span>
<span>public</span> <span>void</span> <span>testIndex</span><span>()</span> <span>throws</span> <span>Exception</span> <span>{</span>
<span>try</span><span>(</span><span>RxHttpClient</span> <span>client</span> <span>=</span> <span>embeddedServer</span><span>.</span><span>getApplicationContext</span><span>().</span><span>createBean</span><span>(</span><span>RxHttpClient</span><span>.</span><span>class</span><span>,</span> <span>embeddedServer</span><span>.</span><span>getURL</span><span>()))</span> <span>{</span>
<span>assertEquals</span><span>(</span><span>HttpStatus</span><span>.</span><span>OK</span><span>,</span> <span>client</span><span>.</span><span>toBlocking</span><span>().</span><span>exchange</span><span>(</span><span>"/hello"</span><span>).</span><span>status</span><span>());</span>
<span>}</span>
<span>}</span>
<span>}</span>
<span>import</span> <span>io.micronaut.context.ApplicationContext</span><span>;</span>
<span>import</span> <span>io.micronaut.http.HttpStatus</span><span>;</span>
<span>import</span> <span>io.micronaut.http.client.RxHttpClient</span><span>;</span>
<span>import</span> <span>io.micronaut.runtime.server.EmbeddedServer</span><span>;</span>
<span>import</span> <span>io.micronaut.test.annotation.MicronautTest</span><span>;</span>
<span>import</span> <span>org.junit.jupiter.api.Test</span><span>;</span>
<span>import</span> <span>javax.inject.Inject</span><span>;</span>
<span>import</span> <span>static</span> <span>org</span><span>.</span><span>junit</span><span>.</span><span>jupiter</span><span>.</span><span>api</span><span>.</span><span>Assertions</span><span>.</span><span>assertEquals</span><span>;</span>

<span>@MicronautTest</span>
<span>public</span> <span>class</span> <span>HelloControllerTest</span> <span>{</span>

    <span>@Inject</span>
    <span>EmbeddedServer</span> <span>embeddedServer</span><span>;</span>

    <span>@Test</span>
    <span>public</span> <span>void</span> <span>testIndex</span><span>()</span> <span>throws</span> <span>Exception</span> <span>{</span>
        <span>try</span><span>(</span><span>RxHttpClient</span> <span>client</span> <span>=</span> <span>embeddedServer</span><span>.</span><span>getApplicationContext</span><span>().</span><span>createBean</span><span>(</span><span>RxHttpClient</span><span>.</span><span>class</span><span>,</span> <span>embeddedServer</span><span>.</span><span>getURL</span><span>()))</span> <span>{</span>
            <span>assertEquals</span><span>(</span><span>HttpStatus</span><span>.</span><span>OK</span><span>,</span> <span>client</span><span>.</span><span>toBlocking</span><span>().</span><span>exchange</span><span>(</span><span>"/hello"</span><span>).</span><span>status</span><span>());</span>
        <span>}</span>
    <span>}</span>
<span>}</span>
import io.micronaut.context.ApplicationContext; import io.micronaut.http.HttpStatus; import io.micronaut.http.client.RxHttpClient; import io.micronaut.runtime.server.EmbeddedServer; import io.micronaut.test.annotation.MicronautTest; import org.junit.jupiter.api.Test; import javax.inject.Inject; import static org.junit.jupiter.api.Assertions.assertEquals; @MicronautTest public class HelloControllerTest { @Inject EmbeddedServer embeddedServer; @Test public void testIndex() throws Exception { try(RxHttpClient client = embeddedServer.getApplicationContext().createBean(RxHttpClient.class, embeddedServer.getURL())) { assertEquals(HttpStatus.OK, client.toBlocking().exchange("/hello").status()); } } }

Enter fullscreen mode Exit fullscreen mode

What do we do have here ?

  • @MicronautTest annotation tells Micronaut that it needs to make the ApplicationContext available for our test
  • We use a constructor injection to inject an embedded server into our test
  • @test is a simple JUnit 5 annotation, nothing new here
  • Using the application context of the embedded server, we will create a simple HTTP Client bean and we configure it to connect to our embedded server (dynamic) url
  • We then simply execute a blocking GET request to test our very first controller method

️ Although this test will work (we are returning a string within an HTTP 200), let’s modify it to test our “hello” string response instead:

<span>@MicronautTest</span>
<span>public</span> <span>class</span> <span>HelloControllerTest</span> <span>{</span>
<span>@Inject</span>
<span>EmbeddedServer</span> <span>embeddedServer</span><span>;</span>
<span>@Test</span>
<span>public</span> <span>void</span> <span>testIndex</span><span>()</span> <span>throws</span> <span>Exception</span> <span>{</span>
<span>try</span><span>(</span><span>RxHttpClient</span> <span>client</span> <span>=</span> <span>embeddedServer</span><span>.</span><span>getApplicationContext</span><span>().</span><span>createBean</span><span>(</span><span>RxHttpClient</span><span>.</span><span>class</span><span>,</span> <span>embeddedServer</span><span>.</span><span>getURL</span><span>()))</span> <span>{</span>
<span>assertEquals</span><span>(</span><span>"Hello Micronaut !"</span><span>,</span> <span>client</span><span>.</span><span>retrieve</span><span>(</span><span>"/hello"</span><span>).</span><span>blockingFirst</span><span>());</span>
<span>}</span>
<span>}</span>
<span>}</span>
<span>@MicronautTest</span>
<span>public</span> <span>class</span> <span>HelloControllerTest</span> <span>{</span>

    <span>@Inject</span>
    <span>EmbeddedServer</span> <span>embeddedServer</span><span>;</span>

    <span>@Test</span>
    <span>public</span> <span>void</span> <span>testIndex</span><span>()</span> <span>throws</span> <span>Exception</span> <span>{</span>
        <span>try</span><span>(</span><span>RxHttpClient</span> <span>client</span> <span>=</span> <span>embeddedServer</span><span>.</span><span>getApplicationContext</span><span>().</span><span>createBean</span><span>(</span><span>RxHttpClient</span><span>.</span><span>class</span><span>,</span> <span>embeddedServer</span><span>.</span><span>getURL</span><span>()))</span> <span>{</span>
            <span>assertEquals</span><span>(</span><span>"Hello Micronaut !"</span><span>,</span> <span>client</span><span>.</span><span>retrieve</span><span>(</span><span>"/hello"</span><span>).</span><span>blockingFirst</span><span>());</span>
        <span>}</span>
    <span>}</span>
<span>}</span>
@MicronautTest public class HelloControllerTest { @Inject EmbeddedServer embeddedServer; @Test public void testIndex() throws Exception { try(RxHttpClient client = embeddedServer.getApplicationContext().createBean(RxHttpClient.class, embeddedServer.getURL())) { assertEquals("Hello Micronaut !", client.retrieve("/hello").blockingFirst()); } } }

Enter fullscreen mode Exit fullscreen mode

We are now ready to run the test using maven wrapper:

> ./mvnw test
> ./mvnw test
> ./mvnw test

Enter fullscreen mode Exit fullscreen mode

Once finished, you should see an output similar to:

[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.stacklabs.micronaut.workshop.agency.HelloControllerTest
23:18:45.538 [main] INFO i.m.context.env.DefaultEnvironment - Established active environments: [test]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.62 s - in com.stacklabs.micronaut.workshop.agency.HelloControllerTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.stacklabs.micronaut.workshop.agency.HelloControllerTest
23:18:45.538 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [test]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.62 s - in com.stacklabs.micronaut.workshop.agency.HelloControllerTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.stacklabs.micronaut.workshop.agency.HelloControllerTest 23:18:45.538 [main] INFO i.m.context.env.DefaultEnvironment - Established active environments: [test] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.62 s - in com.stacklabs.micronaut.workshop.agency.HelloControllerTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------

Enter fullscreen mode Exit fullscreen mode

Running the app

You probably love tests but you might want to run the app, so let’s do that.

️ First let’s compile and package our application:

> ./mvnw clean install
> ./mvnw clean install
> ./mvnw clean install

Enter fullscreen mode Exit fullscreen mode

By default Micronaut will add Shadow Jar dependencies and plugins to our build so it generates two jars, the “original” one that only contain
our application classes, and a self-contained executable “uber-jar” (or fat-jar) that we can deploy as is.

️ Let’s check the output of our build:

> ls -l target/
-rw-rw-r-- 1 user group 13M mars 10 13:14 hello-world-0.1.jar
-rw-rw-r-- 1 user group 11K mars 10 13:14 original-hello-world-0.1.jar
> ls -l target/
-rw-rw-r-- 1 user group  13M mars  10 13:14 hello-world-0.1.jar
-rw-rw-r-- 1 user group  11K mars  10 13:14 original-hello-world-0.1.jar
> ls -l target/ -rw-rw-r-- 1 user group 13M mars 10 13:14 hello-world-0.1.jar -rw-rw-r-- 1 user group 11K mars 10 13:14 original-hello-world-0.1.jar

Enter fullscreen mode Exit fullscreen mode

️ We can now launch our app using the fat-jar and interact with it using our browser or curl:

> java -jar target/hello-world-0.1.jar
13:24:15.361 [main] INFO io.micronaut.runtime.Micronaut - Startup completed in 968ms. Server Running: http://localhost:8080
> curl http://localhost:8080/hello
Hello Micronaut !
> java -jar target/hello-world-0.1.jar
13:24:15.361 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 968ms. Server Running: http://localhost:8080
> curl http://localhost:8080/hello
Hello Micronaut !
> java -jar target/hello-world-0.1.jar 13:24:15.361 [main] INFO io.micronaut.runtime.Micronaut - Startup completed in 968ms. Server Running: http://localhost:8080 > curl http://localhost:8080/hello Hello Micronaut !

Enter fullscreen mode Exit fullscreen mode

That’s it, we now have a working REST application that we can test, compile and run !

Going further…

…with Micronaut

Of course we only created and developed a simple hello-world application so you might be a bit frustrated, but the goal of this article was simply showing how simple it is to both create and develop a Micronaut application.

You may ask yourself: where should I start to dive further into Micronaut framework ? Well it all depends on what you are trying to achieve but here are some hints:

…beyond Micronaut

As stated in the introduction we now have quite a lot of new JVM frameworks that aim to answer today’s microservices needs. Although we won’t compare them to Micronaut in this article (maybe in a future article ?), I’ll still leave some links here in case you want to dig into one of them:


References:

I can only recommend the great Micronaut documentation that provides most of the information you need. You can basically find it in two places:

Micronaut Discovery (3 Part Series)

1 A Micronaut introduction
2 CLI applications with Micronaut and Picocli
3 Native images with Micronaut and GraalVM

原文链接:A Micronaut introduction

© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
Dream most deep place, only then the smile is not tired.
梦的最深处,只有微笑不累
评论 抢沙发

请登录后发表评论

    暂无评论内容