Introduction

Welcome to this lesson on Dependency Injection in Spring Boot. So far, we've covered the basics of Spring and Spring Boot, examined the typical project structure, and delved into the important files within a Spring Boot project. We've also discussed core concepts like Inversion of Control (IoC) and Dependency Injection (DI). In our last lesson, we learned how to create simple beans without dependencies. Today, we’ll build on that foundation by learning how to create beans with dependencies, leveraging Spring’s powerful DI capabilities.

Dependency Injection at a Glance

Dependency Injection is a straightforward concept that becomes incredibly powerful when used correctly. Essentially, if you manually instantiate an object, you have a few approaches:

  • Use the default constructor and instantiate properties via setters.
  • Use a non-default constructor and pass all parameters into it.
  • Use the default constructor with reflection to instantiate private class fields.

Spring simplifies this process by searching for dependencies by type, making it easier to wire dependencies into your classes.

To handle this automatic wiring, Spring uses the @Autowired annotation, which marks the points where dependencies should be injected. In Kotlin, you can use val, var, and property injection effectively.

Constructor-based Implicit @Autowired

Constructor-based DI is the preferred method in Kotlin due to its compatibility with the primary constructor paradigm and alignment with immutability principles. This involves passing all dependencies into the constructor to create an object:

In this example, the Salad class uses a primary constructor where the dependencies lettuce and tomato are injected. The @Autowired annotation is omitted because Spring will automatically use the primary constructor for dependency injection when only one constructor is present. Constructor injection is generally preferred in Kotlin for better readability, immutability, and testability.

Constructor-based Explicit @Autowired

Although Spring boot automatically detects only one constructor, you can explicitly mark the constructor with @Autowired:

Here, the @Autowired annotation explicitly marks the constructor, making it clear where Spring should perform the injection.

Field-based @Autowired With lateinit

Field-based DI involves directly injecting dependencies into class fields:

In this example, @Autowired is placed directly on the properties water and sugar. Note that the lateinit keyword is used to indicate that these properties will be initialized later. However, constructor injection is generally preferred for better testability and encapsulation.

Mixed Approach

Using a mixed approach can be beneficial by injecting essential dependencies through the constructor and optional ones through field injection with setters if needed:

In this example, the Pizza class is annotated with @Component, making it a Spring-managed bean. The class uses constructor-based dependency injection for Dough and Sauce as essential dependencies. Although the @Autowired annotation is not required on the constructor (since Spring will automatically use the primary constructor if only one is present), it can be included for clarity.

Additionally, the cheese property is marked with @Autowired and lateinit, indicating that Spring will inject this dependency after the object's construction. This example demonstrates a mix of mandatory constructor-based injection and field-based injection, providing flexibility in managing dependencies.

Method Dependency Injection with `@Bean`

Spring also allows for method-based dependency injection using the @Bean annotation. This technique involves injecting dependencies via method parameters in Kotlin configuration classes.

Example:

In this example, the sandwich method is annotated with @Bean, and it takes Bread and Cheese as parameters. Spring's application context will automatically resolve these dependencies by calling the bread and cheese methods, respectively, to supply the required beans. This method-based injection is useful for creating bean instances that depend on other beans defined in the same configuration class.

Summary

In this lesson, we explored various Dependency Injection methods in Spring Boot, including setter, constructor, field injection, @Bean method injection, along with a mixed approach combining these methods. These techniques help create modular, testable, and maintainable applications. Up next, you’ll get hands-on practice with these concepts to solidify your understanding.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal