Introduction to Micronaut: A Cloud-native Java Framework

Introduction to Micronaut: A Cloud-native Java Framework

Micronaut offers AOT compilation, reactive NIO and cloud-native support for microservices and serverless development. Could it be your next Java framework? […]

The Spring framework has dominated backend Java development for a long time, but several new frameworks challenge this status quo. Micronaut is one of the most convincing. Developed by the team that created Grails, Micronaut is made for modern architectures.

This article is a practical introduction to Micronaut. We’ll start with a simple RESTful API-based application, refactor it for reactive non-blocking IO (reactive NIO), and then take a quick look at Micronaut’s support for cloud-native development in microservices and serverless architectures.

What’s special about Micronaut

Micronaut offers a whole range of advantages that have been adopted from older frameworks such as Spring and Grails. It is called “native cloud-native”, which means that it was designed from the ground up for cloud environments. Its cloud-native features include environment detection, service detection, and distributed tracing.

Micronaut also offers a new IoC (inversion-of-Control) container that uses an AOT (AOT = ahead-of-time) compilation for a faster start. Aot compilation means that the startup time does not increase with the size of the codebase. This is especially important for serverless and container-based deployments, where nodes are often shut down and restarted as needed.

Micronaut is a polyglot JVM framework that currently supports Java, Groovy and Kotlin and whose support for Scala is in preparation.

Finally, Micronaut supports reactive programming. Developers can use either ReactiveX or Reactor within the framework. As of Micronaut 3, which was released in July 2021, Reactor is the recommended approach. (Note that the new versions do not include Reactive libraries as transitive dependencies).

Getting started with Micronaut

Micronaut is easy to install on any Unix-based system, including Linux and macOS, via SDKMan. On Windows, download the Micronaut Binary and add it to your path.

Once the installation is complete, you will find the mn tool on your command line.

Open a shell and find a suitable place. Enter mn create-app micronaut-idg -build maven. Micronaut supports Gradle and Maven via wrapper, so you don’t have to install the build tool yourself. I prefer Maven. If you prefer Gradle, omit the –build maven flag in the previous command.

If you start the server with mvnw mn:run, you can access http://localhost:8080 / go, and you will get a standard JSON response “not found”.

If you examine the layout of the sample project, it is a standard maven project with a main class under src/main/java/micronaut/idg/Application.Java. Note that the main class runs an embedded server. When you make code changes, the Micronaut development server automatically updates the running application.

Adding a Micronaut Controller

Just like in Spring MVC, you can add controller classes to map URLs to code handlers. Add a class under src/main/java/micronaut/idg/controller/SimpleController. Let’s use this controller to create a text response as shown in Listing 1.

Listing 1. Verwendung eines Micronaut-Controllers

package micronaut.idg.controller; 

import io.micronaut.http.MediaType; 
import io.micronaut.http.annotation.Controller; 
import io.micronaut.http.annotation.Get; 

@Controller("/simple") 
public class SimpleController { 

    @Get(produces = MediaType.TEXT_PLAIN) 
    public String index() { 
        return "A Simple Endpoint"; 
    } 
} 

In Listing 2 you can see how easy it is to create a JSON formatted response.

Listing 2. Die JSON-formatierte Antwort

package micronaut.idg.controller; 

import io.micronaut.http.MediaType; 
import io.micronaut.http.annotation.Controller; 
import io.micronaut.http.annotation.Get; 

import java.util.Map; 
import java.util.HashMap; 

@Controller("/simple") 
public class SimpleController { 
 
    @Get(produces = MediaType.APPLICATION_JSON) 
    public Map index() { 
      Map msg = new HashMap(); 
      msg.put("message", "Eine simple Nachricht"); 
      return msg;   

    } 
} 

Listing 2 demonstrates Micronaut’s intelligent handling of the produces argument of the @Get annotation. In this case, it outputs the JSON formatted response we set.

Adding a Micronaut Service Layer

Micronaut’s IoC implementation is unique under the hood because it runs before, but it’s still a full implementation of the Contexts and Dependency Injection (CDI) specification. That means you can use all the familiar DI annotations you probably know from Spring (like @Inject).

In Listing 3, we are wiring a Service Layer bean to provide a message. In a real application, this class could call a datastore or other remote API through a data access bean.

Create a folder src/main/java/micronaut/idg/service and add the two files from Listing 3 – an interface (Simple) and its implementation (SimpleService).

Listing 3. Erstellen einer einfachen Service Layer Bean

// Simple.java 
package micronaut.idg.service; 

public interface Simple { 
  public String getMessage(); 

} 

// SimpleService.java 
package micronaut.idg.service; 

import jakarta.inject.Singleton; 

@Singleton 
public class SimpleService implements Simple { 
  public String getMessage(){ 
    return "A simple service message"; 

  } 
} 

Now you can use your new service layer by injecting the service into the SimpleController you created in Listing 1. Listing 4 shows the constructor injection.

Listing 4. Injizieren der Service-Bean in den Controller

@Controller("/simple") 
public class SimpleController { 

  @Inject 
  private final Simple simpleService; 

  public SimpleController(@Named("simpleService") Simple simple) {  // (1) 
    this.simpleService = simple; 

  } 

  @Get(produces = MediaType.APPLICATION_JSON) 
  public Map index() { 
    Map msg = new HashMap(); 
    msg.put("message", simpleService.getMessage()); 
    return msg; 
  } 
} 

The crucial work is done in the line commented with “(1)”, where the service bean is wired with its name. If you are now http://localhost:8080/simple visit, you will see the response of the service layer: {“message”: “A simple message”}

Reactive NIO with Micronaut

Next, we want to study the use of micronaut with Reactor. In this case, we will simply redesign our current application to use Reactor and non-blocking IO. The application performs the same task, but uses a non-blocking stack reactor, and Netty – under the hood.

As mentioned, Micronaut 3 does not include a reactive library by default, so first add the Reactor core to your Maven POM as shown in Listing 5.

Listing 5. Reactor zur pom.xml hinzufügen

<dependency> 
    <groupId>io.projectreactor</groupId> 
    <artifactId>reactor-core</artifactId> 
    <version>3.4.11</version> 
</dependency> 

Now you can go back to the SimpleController and change it as shown in Listing 6.

Listing 6. Den Controller nicht-blockierend machen

import reactor.core.publisher.Mono; 

//... 

@Get 
  public Mono<map> index() { 
    Map msg = new HashMap(); 
    msg.put("message", simpleService.getMessage()); 
    return Mono.just(msg); 
  } 
} 

As you can see, we simply wrap the same return type (a mapping of string/string) with the Reactor Mono class.

Similar support exists for the reactive use of remote services, allowing you to run an application completely on non-blocking IO.

Using Micronauts CLI to create new components

You can use Micronaut’s command line tool to add components. For example, if you want to add a new controller, you can use the mn add-controller MyController command. This outputs a new controller and the tests for it, as shown in Listing 7.

Listing 7. Erstellen eines neuen Controllers mit der Micronaut-Kommandozeile

mn create-controller MyController 
| Rendered controller to src/main/java/micronaut/idg/MyControllerController.java 
| Rendered test to src/test/java/micronaut/idg/MyControllerControllerTest.java 

Cloud-native development with Micronaut

I mentioned earlier that Micronaut was built from the ground up for cloud-native microservices and serverless development. One cloud-native concept that Micronaut supports is Federation. The idea of a federation is that several smaller applications share the same settings and can be deployed in tandem. If this sounds a lot like a microservices architecture, you’re right. The goal is to simplify the development of microservices and keep them manageable. You can learn more about federated services in the Micronaut documentation.

Micronaut also makes it easy to use cloud environments for deployment. For example, you can target the Docker registry of Google Cloud Platform, as shown in Listing 8.

Listing 8. Bereitstellen einer Micronaut-Anwendung mithilfe der Docker-Registry von GCP

./mvnw deploy  
     -Dpackaging=docker  
     -Djib.to.image=gcr.io/my-org/my-project:latest 

In this case, the project would be loaded into the GCP docker registry as a docker image. Note that we used the Jib Maven plugin, which turns a Java project into a Docker image without you having to create an actual docker file.

Also note that we have specified Docker as a packaging tool with -Dpackaging=docker. Once the packaging is complete, you can deploy your project using the GCP command line tool, as shown in Listing 9.

Listing 9. Ausführen des Docker-Images über die Kommandozeile

gcloud run deploy  
    --image=gcr.io/my-org/my-project:latest  
    --platform managed  
    --allow-unauthenticated

Tracing is another cloud-native feature that Micronaut supports. Micronaut, for example, makes it pretty easy to enable Jaeger’s distributed tracing via annotations.

In Listing 10, Jaeger is configured so that all requests are in the application.xml file of a microservices application.

Listing 10. Jaeger-Konfiguration in application.xml

tracing: 
  jaeger: 
    enabled: true 
    sampler: 
      probability: 1 

Conclusion

Micronaut offers a number of features that are great for cloud-native and microservice development. At the same time, the framework makes the more traditional API-based development straightforward and simple. And it integrates well with Reactor and Netty for reactive NIO.

Micronaut can be used alongside Quarkus, Dropwizard and other cloud-native Java frameworks. It is a refreshing alternative to existing solutions.

*Matthew Tyson is the co-founder of the Dark Horse Group, Inc. He believes in technology where people come first. When he’s not playing the guitar, Matt explores the hinterland and the philosophical realms. He has been writing for JavaWorld since 2007.

Development Outsourcing | Unreal Outsourcing

Ready to see us in action:

More To Explore

IWanta.tech
Logo
Enable registration in settings - general
Have any project in mind?

Contact us:

small_c_popup.png