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()
}
Micronaut Dependency Injection Types
Constructor, Field, and method parameter injection.
Authors: Sergio del Amo
Micronaut Version: 3.9.2
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.
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
.
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.
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.