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 are 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, and getting rid of most of the runtime overheads like classpath scanning and dynamic class loading. Therefore, it is a framework of the future, focusing on the 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 that 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.
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. Surely, it follows, that a framework built by an esteemed developer like him would meet the criteria of reliability and performance that is expected of modern web frameworks.
Most java frameworks, including Spring, use reflection for implementing 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. 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 feature of Java, 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, their methods and instance variables as well, and so on) are fetched at runtime and stored in the 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 size of the code (or the number of lines increases) the runtime overhead and consumption of memory increases.
The Spring framework relies heavily on reflection and uses bean introspection for managing most of its features. 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’s still using reflection behind the scenes to handle the introspection. Moreover, this is done at runtime so it adds a burden on memory and adds unnecessary runtime overhead for introspecting the beans.
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 that are needed by the code. In this way, all the references that are needed by your code are computed at compile time, so there is no overhead or extra memory needed at runtime. 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<Book> 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<Book> 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 are the files that Micronaut generates at compile time to handle the introspection and these 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.
Consider this scenario. You are developing a large-scale web application that uses tons of annotations from different packages. It is very likely that some of the annotations would have the same name in different packages. In a scenario like this, sooner or later you’ll start running into annotation conflicts. The most common example of this is the @Nullable annotation which is present in javax. annotations, org. spring framework.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.
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.
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 it’s really easy to switch to this implementation for an existing system.
Micronaut Data JDBC removes the reliance on Hibernate completely 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 of choosing between any annotation type. 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.
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. This also means that all the features that are available in Spring WebFlux can now be used in Micronaut with minimal changes.
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.
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.
It usually takes time for a framework to be adopted widely by the community and since Micronaut hasn’t yet had enough time to mature, the best source of information about it is in their official documentation. Therefore, if you get stuck at a unique problem, it can get difficult to search for solutions.
Unlike Spring, it can get 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.
Micronaut provides support for popular caching and application monitoring frameworks like Redis and Caffeine, but the support is not as wide as mature frameworks like Spring.
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 it’s a recently developed technology, it still lacks some important features. But with time it is expected to become more mature, and a framework of choice for most web developers.
USA408 365 4638
1301 Shoreway Road, Suite 160,
Belmont, CA 94002
Whether it’s a brand refresh, public relations push, new website or end-to-end
behaviour change campaign —
we’re interested and ready to talk solutions.
COMMENTS (0)
Tweet