MICRONAUT under the hood

koolak82
4 min readNov 14, 2019

--

This is an attempt to present (an incomplete) working of MICRONAUT under the hood. Micronaut is the micro framework for cloud microservices in Java which has Dependency Injection at core and is suitable for various different kind of applications like non blocking HTTP REST services, event based microservices, function (lambda) microservices, even batch etc. It’s not just for Java either. It has out of the box Groovy, and Kotlin. And it does all these at compile time as compared to current breed of runtime, reflection based similar popular (and leading) java frameworks of present, most notably Spring.

There has been an enhancement request raised , https://github.com/micronaut-projects/micronaut-core/issues/2322, in micronaut-projects/micronaut-core for the authoritative take on the architecture. The current simple piece (this story) is my attempt to add small bit of it’s inner workings and major concepts.

Also restricted to Java bit.

Annotation Processors

The first major concept in micronaut is Annotation Processing. Annotation Processing is the tool through which micronaut does all the work at compile time and also further it doesn’t leave anything for the runtime (in other words doesn’t use reflection). Micronaut Core Annotation Processing does a compile time implementation of Java DI specs (again a standard now and incorporated a fair bit from older DI frameworks like Spring, and Google Guice etc).

So, getting back to Annotation Processing, and specifically compile time Annotation Processing, Java got that from Java 6 onwards. Annotation Processing is feature of Java platform which takes some java code and outputs the new / different java source code file with it. This annotation processor doesn’t change or modify the input file.

Also it runs in a separate JVM of it’s own.

For Annotation Processor to work one has to register it’s annotation processors, a specific file to be used, javax.annotation.processing.Processor which is in the micronaut case is:

micronaut-core/inject-java/src/main/resources/META-INF/services/javax.annotation.processing.Processor

And this file has a specific format, it needs to have newline delimited processors (which can be more than one) which contains all of your processors. These are the specifics used in micronaut

io.micronaut.annotation.processing.TypeElementVisitorProcessorio.micronaut.annotation.processing.PackageConfigurationInjectProcessorio.micronaut.annotation.processing.BeanDefinitionInjectProcessor

I will try to go through the BeanDefinitionInjectionProcessor. ABean in micronaut is represented as BeanDefinition and hence the process looks to be processing all of those and creating BeanDefinition. Ana just a brief addon, this BeanDefinitionInjectionProcessor work with following annotation types (not full list):

ProcessedTypes.POST_CONSTRUCT,//"javax.annotation.PostConstruct"
ProcessedTypes.PRE_DESTROY, // "javax.annotation.PreDestroy"
"javax.inject.Inject",
"javax.inject.Qualifier",
"javax.inject.Singleton",
"io.micronaut.context.annotation.Bean",
"io.micronaut.context.annotation.Replaces",
"io.micronaut.context.annotation.Value",
"io.micronaut.context.annotation.Property",
"io.micronaut.context.annotation.Executable"

So as clear, it provides a container for DI, in other words is an implementation of Java EE CDI specs. It would be difficult to go through each one of them. Each would need substantial paragraphs of their own. Plus the intention of the story is to pen down the high level architecture, so let’s go with the flow. (Having said that, I might visit some of them in next section).

It’s also of some use to point out that BeanDefinitionInjectionProcessor has a hierarchy and extends AbstractInjectAnnotationProcessor. This class does the work of base init() and getSupportedSourceVersion.

BeanDefinitionInjectionProcessor add the important working bit in the init()

super.init(processingEnv);        
this.metadataBuilder = new JavaConfigurationMetadataBuilder (elementUtils, typeUtils, annotationUtils);
this.beanDefinitions = new LinkedHashSet<>(); // the end result

Like, the last line shows, we are initialising the beanDefinitions in the init() and we should be having the LinkedHashSet populated by the end of it.

The most important piece of an Annotation Processor is the process() method. This is where the crux of the processing takes place. As a pseudo code description, it does

process:
filterNonKotlinAnnotation
processNonGroovyNonInterfaceAnnotationsAndIntroductionAnnotations // Need to explore more what is Introduction type
processAllBeanDefinitions // An inner class AnnBeanElementVisitor is used for the processing actually
// beanDefinitionReferenceWriter and beanDefinitionWriter instances are used internally in processing

Taking a small pause here for now. I wanted to cover much much more. But this thing is already too complex for my small head. Just a peeking and some exploration ahead I have already done and can tell you that beanDefinitionReferenceWriter and beanDefinitionWriter use ASM library and that is what does the code generation part. Already there seems some basic magic going on in Annotation Processor. ASM is next level of iteration on that. As such I wanted to cover some bit about these topics and may return on these

  • Dependency Injection, CDI
  • Core Classes and Components in micronaut and how they interact, as you can guess from my enhancement request, this would be very useful for a newbie to micronaut who wants to know internal.
  • And ASM too. What’s in store there?
  • And last but not the least, may be fill the gaps I have identified in this post itself or corrections. So, like some thing more on introduction, other Processors, what about Groovy and Kotlin (but this is a lower priority).

Happy MIRCRONAUT-ing!

Update 20/11/2019: I have followed this up with MICRONAUT under the hood — Part II Not sure when this will be completed or when it should be a series. Till then. You can follow it up with cross links.

--

--

koolak82

(Sphagetti) Javascript, (Bloated) Java .. in the middle, and (frightening, mysterious) DB at the back