Micronaut Dependency Injection Types

Constructor, Field, and method parameter injection.

Authors: Sergio del Amo

Micronaut Version: 4.4.0

1. Sample Project

You can download a sample application with the code examples shown in this article.

2. Introduction

What is a dependency? Class A has a dependency on Class B when it interacts with it in any way. For example, Class A executes a method in an instance of Class B. You typically don’t instantiate class dependencies when using Micronaut Framework’s dependency injection engine. Instead, you rely on the framework to provide dependency instances.

In Micronaut Framework documentation, we often use the term Bean and Dependency interchangeably.

3. JSR-330

Micronaut Framework implements the JSR-330 - Dependency Injection for Java specification.

4. jakarta.inject vs javax.inject

Early versions of Micronaut Framework used javax.inject annotations. Due to trademark restrictions imposed on the javax.* namespace, the Micronaut framework switched from the javax.inject to the jakarta.inject annotations. Since version 2.4, the Micronaut framework supports jakarta.inject annotations. Moreover, Micronaut Framework switched to jakarta.inject as the set of annotations included by default, and we strongly encourage you to use jakarta.inject going forward.

5. Dependency Injection Types

Micronaut Framework offers three types of dependency injection:

5.1. Constructor Injection

In constructor-based injection, Micronaut Framework provides the dependencies required for the class as arguments to the constructor.

src/main/kotlin/example/micronaut/constructor/MessageController.kt
package example.micronaut.constructor

import example.micronaut.MessageService
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.Produces

@Controller("/constructor")
class MessageController(val messageService: MessageService) {
    @Get
    @Produces(MediaType.TEXT_PLAIN)
    fun index() = messageService.compose()
}

If your class has multiple constructors, the Micronaut framework searches for one constructor annotated with jakarta.inject.Inject.

5.2. Field Injection

With field injection, Micronaut fulfills the injection endpoints for fields annotated with jakarta.inject.Inject.

src/main/kotlin/example/micronaut/field/MessageController.kt
package example.micronaut.field

import example.micronaut.MessageService
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.Produces
import jakarta.inject.Inject

@Controller("/field")
class MessageController {
    @Inject
    lateinit var messageService: MessageService

    @Get
    @Produces(MediaType.TEXT_PLAIN)
    fun index() = messageService.compose()
}

Field injection makes it harder to understand a class’s requirements, making it easier to get a NullPointerException when testing a class using Field Injection. We recommend you use Constructor Injection.

The previous code sample uses a field with the default access modifier. There are four types of access modifies available in Java: private, protected, public, and the default. Field injection works with all of them. However, Field injection in a field with private access modifier requires reflection. Thus, we recommend you not to use private.
You use field injection in test classes annotated with MicronautTest. Micronaut does not instantiate the test class. Thus, you cannot use Constructor Injection in those classes.
When using Micronaut for Spring, you can also use Spring’s @Autowired annotation for field injection. To learn more, read the guide: Run a Spring Boot application as a Micronaut application.

5.3. Method parameter injection

For method parameter injection, you define a method with one or more parameters and annotate the method with the jakarta.inject.Inject annotation.

src/main/kotlin/example/micronaut/methodparameter/MessageController.kt
package example.micronaut.methodparameter

import example.micronaut.MessageService
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.Produces
import jakarta.inject.Inject

@Controller("/setter")
class MessageController {
    var messageService: MessageService? = null

    @Get
    @Produces(MediaType.TEXT_PLAIN)
    fun index() = messageService?.compose()

    @Inject
    fun populateMessageService(messageService: MessageService) {
        this.messageService = messageService
    }
}

5.4. Benefits of Constructor Injection

When possible, we recommend you use Constructor Injection for several reasons:

Clear Contract

Constructor injection clearly expresses the requirements of the class and requires no additional annotation.

Immutability

Constructor injection allows you to define final dependencies. Thus, creating immutable objects.

Identifying code smells

Constructor injection helps you easily identify if your bean depends on too many other objects.

Testing

Constructor injection simplifies writing both unit and integration tests. The constructor forces us to provide valid objects for all dependencies. Thus, it decreases the chance of a NullPointerException occurrence during testing.

6. Next steps

Read more about Micronaut Inversion of Control capabilities.

7. Help with the Micronaut Framework

The Micronaut Foundation sponsored the creation of this Guide. A variety of consulting and support services are available.

8. License

All guides are released with an Apache license 2.0 license for the code and a Creative Commons Attribution 4.0 license for the writing and media (images…​).