3.3 KiB
Micronaut Conversion Guide
When This Applies
This guide applies when the Java source contains imports matching io.micronaut.*.
This covers Micronaut HTTP, Micronaut Data, and Micronaut Security.
Key Rules
1. Constructor injection is the default
Micronaut uses compile-time dependency injection via constructor injection by default.
This maps naturally to Kotlin's primary constructor. Remove @Inject when there is
only one constructor — Micronaut discovers it automatically.
2. Stereotype annotations
@Singleton, @Controller, @Client, @Repository — preserve these exactly.
No annotation site target is needed.
3. @Value annotation
Escape $ in Kotlin to prevent string template interpretation:
@Value("\${config.key}") val configKey: String
4. @Inject field injection → constructor injection
Replace @Inject on fields with constructor parameters in Kotlin's primary constructor.
This eliminates lateinit var and makes dependencies immutable.
5. AOP interceptors require open classes
Classes using AOP annotations (@Around, @Introduction, @Cacheable) must be open
in Kotlin because Micronaut generates subclass proxies for them at compile time.
6. Bean factories
@Factory classes and their @Bean-annotated methods should be open so Micronaut
can manage their lifecycle through subclassing.
7. @ConfigurationProperties
Convert to a class with mutable properties. Use lateinit var for required String
properties and var with defaults for primitives. The class must be open.
Examples
Example 1: Micronaut Controller with Constructor Injection
Java:
package com.acme.web;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.PathVariable;
import jakarta.inject.Inject;
@Controller("/api/orders")
public class OrderController {
private final OrderService orderService;
private final InventoryClient inventoryClient;
@Inject
public OrderController(OrderService orderService, InventoryClient inventoryClient) {
this.orderService = orderService;
this.inventoryClient = inventoryClient;
}
@Get("/{id}")
public OrderDto getOrder(@PathVariable Long id) {
return orderService.findById(id);
}
@Get
public List<OrderDto> listOrders() {
return orderService.findAll();
}
}
Kotlin:
package com.acme.web
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.PathVariable
@Controller("/api/orders")
class OrderController(
private val orderService: OrderService,
private val inventoryClient: InventoryClient
) {
@Get("/{id}")
fun getOrder(@PathVariable id: Long): OrderDto? {
return orderService.findById(id)
}
@Get
fun listOrders(): List<OrderDto> {
return orderService.findAll()
}
}
Key changes:
@Injectis removed — Micronaut auto-discovers the single constructor.- The
jakarta.inject.Injectimport is removed because it is no longer referenced. - Constructor parameters become
private valin the primary constructor. - Return type
OrderDtobecomesOrderDto?where the service may return null.