rgoussu@goussu: ~/library/java/frameworks
~/library/java/frameworks cat micronaut.md

Micronaut deep dive

# Micronaut in depth — compile-time DI and AOP with no reflection, Micronaut Data, declarative HTTP clients and GraalVM affinity.

Conceptsaved 2026-08-09 #java#frameworks#micronaut#graalvm#cloud-native

Overview

Micronaut (originally from the Grails team at Object Computing) takes the build-time idea to its logical end: dependency injection, AOP and configuration binding are computed entirely by annotation processors at compile time. There is no runtime reflection, no classpath scanning and no runtime proxies — the framework's startup cost is near-constant regardless of codebase size. That design predates and anticipated GraalVM native image, which is why Micronaut apps compile to native binaries with almost no extra configuration.

Key points

  • Compile-time DI: micronaut-inject-java annotation processor generates BeanDefinition classes at compile time; injection uses JSR-330 annotations (@Inject, @Singleton) — errors surface at compilation, not boot.
  • Compile-time AOP: @Around/@Introduction advice woven by generating subclasses during compilation — no CGLIB, no self-invocation trap of the Spring kind.
  • Bean introspection: @Introspected generates reflection-free accessors for serialization, validation and data binding (micronaut-serde replaces Jackson's reflection use).
  • Micronaut Data: repository queries computed and validated at compile time — contrast with Hibernate/Spring Data building queries at runtime; JDBC/R2DBC flavors, or JPA via Hibernate when full ORM is needed.
  • Declarative HTTP client: @Client("/users") on an interface generates the implementation at compile time.
  • Cloud-native built-ins: environment-aware configuration, service discovery (Consul, Eureka), distributed tracing, and first-class serverless via micronaut-function-aws (Lambda) and GCP/Azure equivalents.
  • Positioning: own APIs on a JSR-330/Jakarta lineage rather than MicroProfile — the main philosophical split with Quarkus.

Details

Compile-time machinery

Every injectable bean gets a generated BeanDefinition (and @Introspected types a BeanIntrospection) written during javac by annotation processors — Kotlin (KSP) and Groovy are equally supported. Consequences: missing dependencies fail the build; startup does no scanning (fastest JVM cold start of the big three); memory stays flat; and the entire framework is native-image-friendly because there is nothing dynamic left to configure. The cost is compile-time magic: build errors in generated code and processor configuration replace Spring's runtime stack traces.

AOP without proxies at runtime

@Around advice (e.g. @Cacheable, @Retryable, @Transactional) is implemented by compile-time-generated intercepted subclasses. Because weaving happens in the compiler, the advised class is what actually gets instantiated — Spring's proxy/self-invocation pitfall class largely disappears, though final methods still can't be advised.

Micronaut Data

micronaut-data-jdbc (or -r2dbc) is the signature module: repository interfaces like Spring Data's, but the SQL is generated and checked at compile time — a typo'd findByEmial is a compile error, and there is no runtime query-metamodel cost. micronaut-data-hibernate-jpa slots in Hibernate when entity-graph ORM features are required. Bean Validation is honored via micronaut-validation (Jakarta Validation annotations, reflection-free).

HTTP server & client

The server (micronaut-http-server-netty) is Netty-based with optional virtual-thread event-loop offloading; controllers use Micronaut's own annotations (@Controller, @Get). The declarative client mirrors the server API and gets load-balancing and service discovery for free — point @Client(id = "inventory") at a discovered service.

Micronaut vs Quarkus

Both are build-time, GraalVM-first frameworks; the split is philosophical. Quarkus bets on standards (CDI, JAX-RS, MicroProfile — SmallRye implementations) and Red Hat's enterprise channel; Micronaut ships its own coherent APIs with JSR-330 at the core, plus the compile-time Data layer Quarkus has no direct equivalent of (Panache still runs Hibernate at runtime). Team background usually decides: Jakarta EE refugees find Quarkus familiar; Spring refugees find Micronaut's API shapes familiar.

Examples

@Controller("/orders")
public class OrderController {
  private final InventoryClient inventory;
  OrderController(InventoryClient inventory) { this.inventory = inventory; }

  @Get("/{id}")
  public Order get(Long id) { ... }
}

@Client(id = "inventory")                      // implementation generated at compile time
interface InventoryClient {
  @Get("/stock/{sku}") int stock(String sku);
}

@JdbcRepository(dialect = Dialect.POSTGRES)    // SQL generated & validated at compile time
interface OrderRepository extends CrudRepository<Order, Long> {
  List<Order> findByCustomerId(Long customerId);
}

Related