💉Dependency Injection in Spring: Constructor, Property, or Setter? Discover the Best Option for Your Project!

Miguel Angel Perez Diaz
6 min readApr 5, 2023

--

Dependency injection is a programming technique in which objects are created with all the dependencies they need to function properly, rather than creating those dependencies within the object.

Dependency Injection is a key feature of the Spring Framework, allowing for loosely coupled, reusable code. But with multiple ways to inject dependencies in Spring, it can be challenging to know which approach to use for your project. In this article, we’ll explore the three primary Dependency Injection techniques in Spring. We’ll discuss the benefits and drawbacks of each approach, and provide guidance on when to use each method.

In the Spring Framework, there are three main dependency injection techniques: Constructor Based Dependency Injection, Field or Property-Based Dependency Injection (@Autowired in a variable) and Setter Based Dependency Injection. We will now discuss which of these techniques is the best option in different situations.

By the end of this article, you’ll have a solid understanding of how to choose the best Dependency Injection technique for your specific Spring project.

🧱 Constructor Based Dependency Injection

Constructor Based Dependency Injection is the most recommended dependency injection technique in the Spring Framework. This technique involves passing all necessary dependencies for an object as arguments to the constructor. The object is created only after all necessary dependencies have been provided.

The main reason for preferring dependency injection through the constructor is that it allows dependencies to be explicit and mandatory. This means that all dependencies necessary for an object to function correctly must be provided at object creation time, which ensures that the object is in a valid state from the start. In addition, injecting dependencies through the constructor makes the code easier to understand and maintain, as all dependencies required for the object are clearly defined in the constructor.

Let’s look at an example:

public class ProductService {
private final ProductRepository productRepository;

public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
}

In this example, the ProductService class has a dependency on ProductRepository , which is passed as an argument in the constructor.

In the example I mentioned above, you don’t need to use the @Autowired annotation when using dependency injection through the constructor. This is because Spring Framework can automatically detect the constructor with arguments and provide the corresponding dependency instances at runtime.

So, in the example I showed, Spring Framework will detect that the ProductServiceclass has a constructor with an argument of type ProductRepository, and will look for a bean of type ProductRepositoryto inject. Since the ProductRepositoryis defined as a bean in the Spring context, an instance of ProductRepositorywill be created and injected into the constructor of ProductService.

Besides the finalkeyword is used to declare the variable productRepositoryas a constant. This way, once a value is assigned in the constructor, the variable cannot be reassigned to another object.

The use of the finalkeyword is optional when injecting dependencies through the constructor, but it is a good programming practice because it helps ensure the immutability and consistency of objects. By making a variable final, you avoid accidentally changing its value in other parts of the code.

Therefore, although the use of finalis not mandatory when injecting dependencies through the constructor, its use is strongly recommended whenever possible. Furthermore, it is also important to note that dependency injection through the constructor works regardless of whether the variable is final or not.

Tip ✨

When using this type of dependency injection we can rely on the Lombok library, which offers us the @RequiredArgsConstructor annotation that simplifies the code and saves us from generating the class constructor with the different necessary parameters.

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductRepository productRepository;
}

In this example, Lombok will automatically generate a constructor that takes a ProductRepositoryargument and assigns it to the productRepositoryvariable. There is no need to write the constructor manually, which reduces the amount of code that needs to be written.

It is important to note that Lombok uses the @NotNull annotation for fields in the class that are declared as final, which means that the field cannot be null. If the argument passed to the constructor is null, a NullPointerExceptionwill be raised.

🧩 Field or Property-Based Dependency Injection

Property-Based Dependency Injection(@Autowired on a variable) involves annotating a property with the @Autowired annotation. When Spring creates an object that has a property annotated with @Autowired, Spring looks up an instance of the corresponding dependency and assigns it to the property.

Injecting dependencies through properties can make dependencies optional, which means that the object may be in an invalid state until all dependencies are set. This can cause run-time errors and make it difficult to identify the root cause of the problem.

Let’s look at an example:

public class ProductService {

@Autowired
private ProductRepository productRepository;
}

In this example, the ProductServiceclass has a dependency on ProductRepository, which is injected through the productRepositoryproperty annotated with (@Autowired).

🎯 Setter Based Dependency Injection

Setter Based Dependency Injection involves annotating a method with the @Autowired annotation. When Spring creates an object that has a method annotated with @Autowired, Spring looks up an instance of the corresponding dependency and assigns it to the method parameter.

Like the previous one, Setter-based injection can be useful in situations where optional properties are needed or when it is difficult to modify the constructor of an existing class. However, it also has some disadvantages.

One of the main disadvantages of setter-based injection is that it can lead to objects in an inconsistent state if all required dependencies are not established. This can be problematic because the object may not function correctly if methods are used before all dependencies have been established.

Another disadvantage is that setter-based injection can make it difficult to understand the dependencies of an object in the code, since the dependencies are not explicitly specified in the constructor. This can make it more difficult to follow the flow of control through the code.

Let’s look at an example:

public class ProductService {
private ProductRepository productRepository;

@Autowired
public void setProductRepository(ProductRepository productRepository) {
this.productRepository = productRepository;
}
}

In this example, the ProductServiceclass has a dependency on ProductRepository, which is injected through the setProductRepository() method annotated with (@Autowired).

🧠 Choosing a Dependency Injection Technique

Now that we have covered the three main dependency injection techniques in the Spring Framework, how do we choose which one to use? In general, it is recommended to use dependency injection through the constructor whenever possible. This makes dependencies explicit and mandatory, leading to cleaner code that is easier to understand and maintain. In addition, injecting dependencies through the constructor avoids the potential problems of property or method-based dependency injection techniques.

However, there are cases where dependency injection through properties or setters can be useful. For example, if you have a class with many dependencies and you want to keep your code more readable, it may be useful to inject dependencies through properties instead of a constructor with many arguments. It may also be useful to use dependency injection through methods if you want to establish the dependency after the object is created.

In summary, dependency injection via the constructor is the most recommended dependency injection technique in the Spring Framework. Dependency injection techniques based on properties and setters can be useful in certain situations, but it is important to be aware of the potential problems that may arise. By choosing the right dependency injection technique, you can ensure that your code is clean, easy to understand and maintain, and in a valid state from the start.

I hope this post will help you to better understand the different types of dependency injection and to write much more readable code with good practices. Happy coding! 🚀🎉

--

--

Miguel Angel Perez Diaz

I am a Software Engineer and currently work as a Backend Developer. I like to research about new technologies and share any knowledge or tips that can help.