1. Overview

In this tutorial, we’ll learn about the different Spring dependency injection options available with Kotlin. Then, we’ll go through some examples as well.

2. Spring @Autowired

We know that since v2.5, Spring’s dependency injection facility provides a convenient annotation @Autowired.

The annotation-based configuration depends on bytecode metadata for injecting components. In other words, the Spring framework’s implementation of the Inversion of Control (IoC) principle (or Dependency Injection) uses Java reflection to inject the dependency.

Moreover, to indicate a bean wiring, we can configure the component class using @Autowired annotation on the component class, method, or field declaration.

3. Constructor-based Implicit @Autowired

Since Spring v4.3, we can omit the @Autowired annotation on a constructor if the target bean defines only one constructor.

However, if there are multiple constructors and no primary/default constructor, then at least one of the constructors must be annotated with @Autowired to instruct the container which one to use.

Similarly, let’s suppose a class has multiple constructors with one of them being the default constructor. Now if none are annotated with @Autowired, then the container uses the default constructor for bean creation and dependency injection.

Let’s see an example of an implicit injection of the PersonService bean into the PersonController constructor without specifying @Autowired:

@RestController
class PersonController(val personService: PersonService)

The below example is the case where LocationService has multiple constructors with one of them being the default constructor. Notably, the annotation @Autowired is missing from both constructors. But since it has a default constructor, the container uses it to create the LocationService bean and then injects it into PersonController we defined earlier:

@Service
class LocationService() {
    var lat: Double = 109.344550
    var lon: Double = 133.973849

    constructor(lat: Double, lon: Double) : this() {
        this.lat = lat
        this.lon = lon
    }
}

Here is an example where the target PersonService bean doesn’t have a default constructor. On the other hand, it has a couple of overloaded constructors which vary in the arguments they take:

@Service
class PersonService {
    var person: Person
    lateinit var address: Address

    constructor(person: Person) {
        this.person = person
    }

    @Autowired
    constructor(person: Person, address: Address): this(person) {
        this.address = address
    }
}

It’s worth noting that we have @Autowired only one of the constructors. That constructor takes in two arguments, a Person and an Address beans. Consequently, we have instructed the container to use this constructor while creating the PersonService bean.

4. Constructor-based Explicit @Autowired

Another option in Spring for bean wiring is to annotate the constructor of the component with @Autowired explicitly:

@Service
class AddressService @Autowired constructor(val address: Address)

In the above example, we’ve used @Autowired annotation explicitly in the constructor to inject the Address bean.

5. Field-based @Autowired With lateinit

Let’s see how we can inject beans using @Autowired along with lateinit.

We can apply @Autowired to a field and also mix it with constructor-based @Autowired:

@Service
class InventoryService @Autowired constructor(
    val vehicleDao: VehicleDao
) {
    @Autowired
    private lateinit var dealerDao: DealerDao

    // ...
}

In the above example, the container injects the VehicleDao bean through the constructor during InventoryService bean creation and later injects the DealerDao bean through the property.

Generally, it’s not recommended to use field-based dependency injection. Instead, constructor-based injection is recommended since we can implement components as an immutable object. This ensures the dependencies aren’t null.

With field-based dependency injection, the component is no more an immutable object. The initial value of the dependencies could be null.

Additionally, in the unit tests, it’s challenging to inject components. We’ve to depend on the dependency injection container or reflection mechanisms.

6. Method-based @Autowired

Additionally, we can apply the @Autowired annotation to the traditional setter method as well:

@Component
class DealerDao {
    @set: Autowired
    lateinit var reviews: DealerReviewsDao

   // ...
}

We can also apply @Autowired annotation to methods with arbitrary names and multiple arguments:

@Component
class VehicleDao {
    lateinit var vehicleValueFinder: VehicleValueFinder
    lateinit var vehicleReviewDao: VehicleReviewDao

    @Autowired
    fun initialize(vehicleValueFinder: VehicleValueFinder,
                    vehicleReviewDao: VehicleReviewDao) {
        this.vehicleValueFinder = vehicleValueFinder
        this.vehicleReviewDao = vehicleReviewDao
    }
}

Here we’ve defined an arbitrary method called initialize() with a couple of beans as arguments and annotated it with @Autowired. As a result, the container injects those beans through the method.

7. Conclusion

In this article, we learned that we can use @Autowired to inject bean dependency in Kotlin.

Additionally, for Spring v4.3 and above, we can use the constructor dependency injection without explicitly using the @Autowired in the case of a single constructor.

We also looked at a few examples of dependency injection through class methods and fields.

As always, code snippets can be found over on GitHub.

Comments are closed on this article!