A Brief Introduction to Micronaut

What is Micronaut?
COMMENTS (0)
Tweet

Since its initial release in May 2018, Micronaut has gained attention and respect for its elegant design, lightning-fast speed, and effortless cloud compatibility. But what is Micronaut? 

According to Graeme Rocher, the creator of Micronaut, it’s an open-source microservices and serverless-focused framework and one of those projects that is really changing the perception of how Java is perceived as being heavy-weight by changing the face of server-side Java. 

As a framework, it tackles the shortcomings of traditional Java frameworks like Spring Boot and Grails by eliminating reliance on reflection and reflective data caches, improving AOT (Ahead of Time Compilation), introducing annotation metadata, reactive streams, and efficient query execution.

Additionally, it gets rid of most of the runtime overheads like classpath scanning and dynamic class loading. Therefore, it is a framework of the future, focusing on cloud, microservices, and much more.

The significance of these improvements packed into a Java-based framework is unprecedented for cloud applications. As all developers know, the cost of a cloud-based application is directly proportional to the amount of storage and computation the application consumes. 

By reducing runtime overheads and caches, Micronaut has made it possible to create lightweight applications that consume significantly less storage and processing power than traditional Java frameworks. 

Therefore, applications built using Micronaut are cheaper and more efficient without any added complexity or compromise on development speed.

Who Created Micronaut?

The credibility of Micronaut is evident from the stunning profile of its creator, Graeme Rocher. Before Micronaut, Graeme created the Grails framework – an open-source web framework that has gained popularity due to its focus on high productivity. 

He is also the co-author of The Definitive Guide to Grails and has played a significant role in the success of the Grails framework.

Graeme is currently working as an architect at Oracle. As a member of the Java Champions and holder of the 2018 GroundBreaker award by Oracle, he pretty much knows everything there is to know about Java. 

Needless to say, a framework built by an esteemed developer like him would surely meet the reliability and performance expected of modern web frameworks.

How Is Micronaut Changing the Future of Web Applications?

Reflection Is the Biggest Nightmare for Java Frameworks

Most Java frameworks, including Spring, use reflection to implement their core features. Reflection allows developers to introspect or examine the code at runtime, and for a long time, it has been considered a major strength of Java. 

Code:

For instance, a simple code for manipulating the properties of an object would look something like this:

public class Book {
   private String name;
   private final String authorName;

   public Book(String name, String authorName, Integer numOfPages) {
       this.name = name;
       this.authorName = authorName;
   }

   public String getName() {
       return name;
   }

   public String getAuthorName() {
       return authorName;
   }

   public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

       // Introspecting class using reflection
       Class c = Book.class;
       Method[] methods = c.getDeclaredMethods();
       Field[] fields = c.getDeclaredFields();

       System.out.println("Methods: ");
       for(Method method: methods){
           System.out.println(method);
       }

       System.out.println("Fields: ");
       for(Field field: fields){
           System.out.println(field);
       }

       // Introspecting an object and calling its methods at runtime
       Book book = new Book("Harry Potter", "J.K. Rowling", 200);

       System.out.println("\nInput method to call: ");
       Scanner scanner = new Scanner(System.in);
       String methodName = scanner.nextLine();

       Method userMethod = book.getClass().getMethod(methodName);

       System.out.println("Result: " + userMethod.invoke(book));
   }
}

Output:

Even though it is a powerful Java feature, it adds unnecessary overhead that is not good for large applications. For instance, if reflection is used to introspect only a single method of a class, all the methods and instance variables (and if these variables also have methods and instance variables, and these variables also have methods and instance variables, and so on) are fetched at runtime and stored in cache. 

The worst part is that the garbage collector doesn’t remove them when they are no longer in use. These references are kept in memory until it’s absolutely necessary to remove them. This results in the creation of large reflective caches at runtime. Therefore, as the code size (or the number of lines) increases, the runtime overhead and consumption of memory increases.

Bean Introspection Using Introspector in Spring

The Spring framework relies heavily on reflection and uses bean introspection to manage most of its features. 

Code:

Bean introspection in spring would look like this:

public class Book {
   private String name;
   private String authorName;

   public Book(String name, String authorName) {
       this.name = name;
       this.authorName = authorName;
   }

   public String getName() {
       return name;
   }

   public String getAuthorName() {
       return authorName;
   }

   public static void main(String[] args) throws IntrospectionException {

       BeanInfo beanInfo = Introspector.getBeanInfo(Book.class);
       System.out.println(beanInfo);
       PropertyDescriptor[] props = beanInfo.getPropertyDescriptors();
       for (PropertyDescriptor p : props) {
           System.out.println(p);
       }
       MethodDescriptor[] meths = beanInfo.getMethodDescriptors();
       for (MethodDescriptor m : meths) {
           System.out.println(m.getName());
       }

   }
}

Output:

The code in Spring looks simpler, but it still uses reflection behind the scenes to handle the introspection. Moreover, this is done at runtime, so it adds a burden on memory and unnecessary runtime overhead for introspecting the beans.

Micronaut’s Solution to Reflection: Ahead of Time Compilation

To avoid problems with reflection, Micronaut does not use reflection anywhere in the framework. Instead, it uses AOT or Ahead of Time compilation to compute all the annotations and dependencies needed by the code. 

In this way, all the references your code needs are computed at compile time, so there is no overhead or extra memory needed at runtime. 

Code:

For instance, a code for performing the same reflection tasks as previous examples would look something like this in Micronaut:

@Introspected
public class Book {
   private String name;
   private String authorName;

   public Book(String name, String authorName) {
       this.name = name;
       this.authorName = authorName;
   }

   public String getName() {
       return name;
   }

   public String getAuthorName() {
       return authorName;
   }

   public void setName(String name) {
       this.name = name;
   }

   public void setAuthorName(String authorName) {
       this.authorName = authorName;
   }

   public static void main(String[] args){

       BeanIntrospection bookIntrospection = BeanIntrospection.getIntrospection(Book.class);

       // Introspecting general information about the class
       Collection<BeanProperty<Book, Object>> properties = bookIntrospection.getBeanProperties();

       System.out.println("Properties:");
       for(BeanProperty<Book, Object> property: properties){
           System.out.println(property);
       }

       // Instantiating and introspecting a specific object

       System.out.println("\nInput name of the property to set: ");
       Scanner scanner = new Scanner(System.in);
       String property = scanner.nextLine();
       System.out.println("Input value to be assigned: ");
       String value = scanner.nextLine();

       Book book = new Book("Harry Potter", "J.K. Rowling");
       BeanWrapper bookWrapper = BeanWrapper.getWrapper(book);
       bookWrapper.setProperty(property, value);

       System.out.println("The updated value for " + property + " is: " + bookWrapper.getRequiredProperty(property, String.class));
   }
}

Notice the @Introspected annotation at the top of the Book class. This annotation tells Micronaut that we’ll need introspection for this class somewhere in the code, so Micronaut should do the necessary AOT compilation to enable it. 

That’s all you need to do to make any bean introspectable. And it’s all done at compile time, so there is no reflection needed. 

Still don’t believe me? Following is a snapshot of the build files:

Notice the $Book$Introspection and $Book$IntrospectionRef files. These files that Micronaut generates at compile time to handle the introspection are added only when the @Introspected annotation is used. 

You can inspect the decompiled versions of these classes to get a better understanding of how these are used at runtime to introspect class elements.

Annotation Metadata

Consider this scenario. You are developing a large-scale web application that uses tons of annotations from different packages. It is likely that some of the annotations would have the same name in different packages. 

In a scenario like this, you’ll start running into annotation conflicts sooner or later. The most common example of this is the @Nullable annotation which is present in javax.annotations, org.springframework.lang and many other packages, and it gets difficult to identify which one should be used. Therefore, Micronaut offers a reliable and easy way to handle such scenarios by using Annotation Metadata.

Annotation Metadata allows the inspection of annotations and their metadata. At compile time, all conflicting annotations are mapped to a single one. Therefore, as a developer, you don’t need to worry about the package from which the annotation is imported. Another powerful tool that is possible through annotation metadata is the AnnotationMapper interface. 

Using this interface, you can annotate or replace annotations dynamically. And since annotation metadata is collected at compile time, annotation mapping is also done at compile time.

Query Execution

Running a query is simple, but creating a query at runtime according to dynamic data is complex and adds runtime overhead. To simplify this, Micronaut does all of this at compile time so that it just needs to run the query at runtime without any overhead. 

Therefore, Micronaut Data is a fast and reliable database access toolkit with various APIs. Micronaut Data provides two solutions for handling queries: Micronaut Data JPA and Micronaut Data JDBC.

Micronaut Data JPA

This implementation uses Hibernate as the ORM (Object Relational Mapping). It generates the queries at compile time and then delegates them to Hibernate for further execution. It is useful for scenarios where we need to utilize the complete features of an ORM. 

Furthermore, all of this can be achieved with JPA annotations, so switching to this implementation for an existing system is easy.

Micronaut Data JDBC

Micronaut Data JDBC removes the reliance on Hibernate and generates queries at compile time. This is the recommended solution according to Micronaut and should be your first choice for static queries. Also, the power of annotation metadata gives you the flexibility to choose between any annotation types. So, you can even use JPA annotations with this solution. 

Since it computes everything at compile time, it needs to know the dialect (or the type of database) for which the queries are to be generated. And just by specifying the dialect on the repositories, you can change query generation to the dialect of your choice. Micronaut Data JDBC supports most of the popular dialects like MySQL, Hibernate, etc.

Reactive Streams

Micronaut uses reactive streams in its internal implementation. Previous releases of Micronaut used RxJava2, but since the release of Micronaut 3, it has removed all dependency on RxJava2 and has shifted to Project Reactor. Furthermore, it no longer exposes any reactive stream implementation by default

This reactive nature of Micronaut makes it quite easy to use reactive streams in our projects. Micronaut recommends using the Project Reactor implementation for implementing these streams. It also means that all the features available in Spring WebFlux can now be used in Micronaut with minimal changes.

Serverless Functions

For ages, Java has been considered inadequate for serverless functions because of its large memory footprint and slow startup time. But with Micronaut, it is now possible to create serverless functions that are fast, efficient, and easy to code. 

Moreover, creating a serverless function using Micronaut is much easier than it sounds. Micronaut Launch can help you generate a project with serverless support in seconds.

Limitations of Micronaut

Even though Micronaut beats almost all Java Frameworks in terms of speed and lower memory consumption, it still has a few limitations as it’s a relatively newer technology compared with traditional frameworks.

Limited Community

It usually takes time for a framework to be adopted widely by the community. Since Micronaut hasn’t yet matured, the best source of information about it is in their official documentation. Therefore, if you get stuck on a unique problem, it can be difficult to search for solutions.

Limited Flexibility in Configuration

Unlike Spring, it can be difficult to set up application configuration for Micronaut. It does provide some new and useful solutions for configuring your application, but it lacks when compared with other frameworks.

Limited Support for Caching and Application Monitoring

Micronaut provides support for popular caching and application monitoring frameworks like Redis and Caffeine. Still, the support is not as wide as mature frameworks like Spring.

Conclusion

Micronaut is a fast, efficient, and reliable framework of the future. With extensive cloud support and a lower memory footprint, it is an ideal choice for cloud-based web applications. Due to its similarities with the Spring framework, any programmer with a background in Spring can start working on Micronaut with minimal training. 

As a recently developed technology, it still lacks some essential features. But with time, it is expected to become more mature and a framework of choice for most web developers.

CALL

USA408 365 4638

VISIT

1301 Shoreway Road, Suite 160,

Belmont, CA 94002

Contact us

Whether you are a large enterprise looking to augment your teams with experts resources or an SME looking to scale your business or a startup looking to build something.
We are your digital growth partner.

Tel: +1 408 365 4638
Support: +1 (408) 512 1812