We’ve talked about Micronaut in the past, and its Ahead-of-Time compilation approach to efficiently use Java in Serverless functions.

Thanks to AOT, Micronaut removes the limitations of Java in that domain, e.g the long startup times that would affect FaaS performance.

We’ve mentioned how Micronaut is actually compatible with Spring, making it possible to write applications that run in both runtimes. That means you can take advantage of the Spring features to create applications adapted to e.g. embedded devices, memory-limited environments and of course function as a service.

Let’s explore how Spring an Micronaut go along, starting from our previous post code base, and migrating it to Spring bit by bit.

Integrate Spring With Micronaut

The Spring @Component Annotation

The first baby step is actually to replace the JSR 330 @Singleton annotation by Spring’s @Component annotation. At compile time, Micronaut will take care of the translation, from Spring to JSR 330.

To start using Spring annotations in a Micronaut app two configuration steps are required:

  1. The first step is to add a dependency to the micronaut-spring JAR. It contains some utility classes and AOT-compiled Micronaut-compatible classes for Spring. It also has a transitive dependency to spring-context, which contains the standard @Component, @Bean, @Configuration, etc. Spring annotations.

    <dependency>
        <groupId>io.micronaut</groupId>
        <artifactId>micronaut-spring</artifactId>
    </dependency>
    
  2. The second step is to let Micronaut process those annotations. To allow that, just add another annotations processor:

    <path>
        <groupId>io.micronaut.spring</groupId>
        <artifactId>micronaut-spring-annotation</artifactId>
        <version>1.0.0.RC1</version>
    </path>
    

At this point, @Singleton can be replaced with @Component, and Micronaut will take care of the “translation” from the latter to the former.

Making Configuration Orthogonal

Autowiring is the process of creating beans, either through the good old XML or by annotating the classes and letting auto-scan find them, and hoping that dependencies between them will be fulfilled. For that to happen, there should be one and only one candidate bean for every dependency. A more rigorous approach would be to make everything explicit, by injecting the required bean manually, instead of letting the container choose.

In Spring, this can be done by creating a configuration class, which is any class annotated with @Configuration. Inside this class, every method annotated with @Bean will contribute its return value as a bean to the context.

Let’s make our configuration orthogonal to the classes themselves:

  1. Remove @Component on classes Foo and Bar
  2. Create the @Configuration-annotated class:
    @Configuration
    public class AppConfig {
    
        @Bean
        public Bar bar() {
            return new Bar(new Foo());
        }
    }
    

This is equivalent to the previous setup from a result point of view, but the concept of the bean is decoupled from its underlying class. For example, one could create another Bar bean named differently.

Use More Spring Features in Micronaut

While focusing only on a subset of Spring, Micronaut supports many of the Spring features, allowing to easily migrate an existing Spring application.

For example, standard Spring dependency-injection works as expected:

@Configuration
public class AppConfig {

    @Bean
    public Foo foo() {
        return new Foo();
    }

    @Bean
    public Bar bar(Foo foo) { // Micronaut will find the Foo bean in the context and inject it
        return new Bar(foo);
    }
}

Likewise, it is possible to externalize the values returned by the Foo and Bar classes into an application.yml file, for easier maintenance.

app:
  bar: "Bar"
  foo: "Foo"

To read such a file, Spring provides the configuration property mechanism: create a class annotated with @ConfigurationProperties, whose structure matches the YAML file, and Spring will fill the values from the file to a class instance for you.

@ConfigurationProperties("app")
public class MicronautConfigProperties {

    private String foo;
    private String bar;

    // Getters and setters    
}

At that point, an instance of that class can be injected into the @Bean-annotated method to get its value, and then be passed to the relevant bean:

@Configuration
public class MicronautConfig {

    @Bean
    public Foo foo(MicronautConfigProperties props) {
        return new Foo(props);
    }
}

public class Foo {

    private final String label;

    public Foo(MicronautConfigProperties props) {
        label = props.getFoo();  // With the previous YAML, this will be initialized with "Foo"
    }

    public String foo() {
        return label;
    }
}

Exposing the Integrated Netty HTTP Server

The next goal in our migration journey is to provide some kind of HTTP endpoint, so that we can send requests to our application. By default, Micronaut offers Netty as an embedded non-blocking HTTP server:

  1. Add the Micronaut Netty module as a dependency:

    <dependency>
        <groupId>io.micronaut</groupId>
        <artifactId>micronaut-http-server-netty</artifactId>
    </dependency>
    
  2. Change the way the application is launched. Because it’s to become an HTTP server, the process shouldn’t end, but wait and listen to requests.

    Add the Micronaut runtime dependency:

    <dependency>
        <groupId>io.micronaut</groupId>
        <artifactId>micronaut-runtime</artifactId>
    </dependency>
    

    Update the main() method:

    public static void main(String[] args) {
        Micronaut.run(Main.class);
    }
    

    Micronaut is able to detect the Netty dependency added previously, and launch the application as a never-ending listening process.

At this point, everything is ready to add endpoints.

I assume you’re already familiar with Spring MVC, but as a quick reminder, to create an endpoint in Spring MVC, annotate a class with @Controller and one of its method with @RequestMapping. Also, one can use dedicated @GetMapping, @PostMapping, etc. annotations for refined semantics regarding the HTTP method.

Finally, to return a serialized object - instead of returning a reference to a view - the @RestController can be used in place of @Controller and additional configuration.

The following code is a sample of such a controller:

@RestController
public class BarController {

    private static final Logger LOGGER = LoggerFactory.getLogger(BarController.class);

    private final Bar bar;

    public BarController(Bar bar) {
        this.bar = bar;
    }

    @GetMapping("/")
    public void root() {
        LOGGER.info(bar.bar());
    }
}

If a GET request is made to the root (/) of the server, the root() method should be called.

This works in the context of a Spring application. To make our application “compliant”, one needs to annotate the Main class with the @SpringBootApplication annotation:

@SpringBootApplication
public class Main {

    public static void main(String[] args) {
        Micronaut.run(Main.class);
    }
}

This also works with any other class situated in the same package - but the convention is to annotate the class that hosts the main() method.

Just as before, Micronaut needs to “translate” those new Spring annotations into its own model. Since they come from other JARs, we need to hook additional annotation-processors into the build process, one for each JAR exposing the annotations:

<path>
    <groupId>io.micronaut.spring</groupId>
    <artifactId>micronaut-spring-boot-annotation</artifactId>
    <version>1.0.0.RC1</version>
</path>
<path>
    <groupId>io.micronaut.spring</groupId>
    <artifactId>micronaut-spring-web-annotation</artifactId>
    <version>1.0.0.RC1</version>
</path>

Returning JSON From the Endpoint

Usually, when a request is made to an endpoint, one expects a response: it might be raw text, an HTLM document, an image, etc. but never void in our simple example. By just changing the void return type to String, and actually returning a String instance, the response wraps the later and it’s displayed to the user:

@GetMapping("/")
public String root() {
    return bar.bar();
}

Even if your architecture is not based on microservices, chances are you’re probably consuming - or even providing - some kind of webservices. Nowadays, those are mostly REST-based, with JSON as the widespread exchange format. In Spring MVC, when an entity is returned, the framework takes responsibility to serialize it to JSON, with the help of Jackson. If some customization is required during serialization, hooks are available for that: annotations on the entity class, custom serializer dedicated to a class, etc. It happens exactly the same way in Micronaut. Let’s change the method signature:

@GetMapping("/")
public Bar root() {
    return bar;
}

If a query is sent to the endpoint at that point, the server will return an error: Error encoding object [com.exoscale.syslog.micronaut.Bar@14b72eb8] to JSON: No serializer found for class com.exoscale.syslog.micronaut.Bar and no properties discovered to create BeanSerializer. The reason is that the Bar class doesn’t follow the JavaBean specification: it has no getters.

In order to fix this error, we need to think about the desired result, something akin to:

{
  "bar":"FooBar"
}

Because we are using Spring MVC, we have out-of-the-box Jackson integration. Jackson offers a way to customize the serialization of specific class via so-called serializers. To register such a serializer, just create a class implementing JsonSerializer and register it as a bean:

@Configuration
public class MicronautConfig {

    @Bean
    public JsonSerializer<Bar> serializer() {
        return new JsonSerializer<Bar>() {
            @Override
            public void serialize(Bar bar, JsonGenerator generator, SerializerProvider provider) throws IOException {
                generator.writeStartObject();
                generator.writeFieldName("bar");
                generator.writeString(bar.bar());
                generator.writeEndObject();
            }
        };
    }

    // Other beans
}

The result of calling the endpoint is now:

{"bar":"FooBar"}

Spring MVC offers a declarative way to format the returned JSON. Just add the following snippet into the application.yml file:

jackson:
  serialization:
    indentOutput: true

Alternatively, if you prefer the properties file format, add jackson.serialization.indentOutput=true to the application.properties file.

With that configuration, the returned JSON now perfectly matches our specifications:

{
  "bar":"FooBar"
}

Conclusion

Micronaut is able not only to work its magic on its own, but also to integrate with Spring, Spring MVC and Spring Boot.

While not all features work (e.g. Spring Data), for many use-cases it will allow you write complete and rich applications using the Spring framework.

Everything described above works perfectly once processed by Graal VM, in order to create native executables permitting to target specific environments requiring small memory footprints and quick start times.

The complete sourcecode is available on Exoscale Labs Github repository.