Now that you have a better idea of what dependency injection is and how it is used to implement inversion of control, it's time to learn how to use it in your application. Spring makes dependency injection simple, and it's time to find out how!
Dependency Injection in Spring
In regard to dependency injection, remember how an injector is responsible for ensuring that a class's dependencies are available for use? Well, in a Spring application, Spring is charged with managing those dependencies, their lifecycles, and injecting them where required.
To make this a reality, Spring uses something called the ApplicationContext. The Spring ApplicationContext is the container that creates and manages objects for your application. As you learned earlier, any object that is created and managed by Spring in the ApplicationContext is referred to as a "bean".
Spring designates a class and its dependencies (beans) as being "wired" together. Any time a bean needs other beans to do its work, those other beans are considered dependencies as well. In wiring (connecting) these beans (dependencies) together, you shift responsibility for managing the dependency from the class itself to the Spring ApplicationContext and have inverted control.
How to use Dependency Injection
In Spring applications, one can inject a dependency in many ways. The three most common of which are constructor, setter, and field injection. Let's go over them one by one.
Constructor Injection
Constructor-based injection allows you to instruct the injection of a class's dependencies through its constructor. In other words, you declare a required dependency, and you also define a constructor that takes said dependency as a parameter. Then, when you need an instance of the class, you let Spring inject the reference to the dependency, and you have yourself constructor injection. The code sample below shows you how:
public class ConstructorInjectionExample {
// required dependency
private Dependency dependency;
// use the constructor to let Spring inject the dependency
public ConstructorInjectionExample(Dependency dependency) {
this.dependency = dependency;
}
}
This code sample assumes that the Dependency bean has already been appropriately registered with the ApplicationContext.
Look Back: In the previous lab, StatusController used this type of DI. Because StatusService contained the @Service annotation, Spring automatically registered it within the ApplicationContext.
When a ConstructorInjectionExample is created, Spring will use the constructor to assign the registered Dependency bean as your required dependency. Now, when you wire ConstructorInjectionExample into a different class, it will have all its dependencies preloaded. Pretty nifty.
Learn by Doing
Package: com.codingnomads.ioc.examples.constructorinjection
- Create a new POJO that represents a laptop component.
- In the LaptopConfiguration class, declare a bean that returns an instance of this new class.
- In the Laptop class, add a field for your new class and add it to the constructor - it will now be injected!
Please be sure to push your work to GitHub when you're done.
Setter Injection
Setter-based injection is very similar to constructor injection in the sense that it is a way for the ApplicationContext to insert beans into other beans. With constructor injection, the dependency is passed in using the constructor, but in setter injection, the dependency is passed using a setter accessor method. Maybe an example will help clarify this:
public class SetterInjectionExample {
// required dependency
private Dependency dependency;
@Autowired
public void setDependency(Dependency dependency) {
this.dependency = dependency;
}
}
This example is similar to the constructor injection example in all ways except the fact that a setter has replaced the constructor. This change means that no constructor can set the dependency field. Spring now needs to use the setter to inject the dependency. But how can the container distinguish between setter methods that are used for day-to-day coding and the ones that are used for dependency injection?
Enter, the @Autowired annotation. This annotation is your key to dependency injection in Spring. With it, you can help Spring distinguish between a dependency that needs to be injected and a field managed by the coder.
Learn by Doing
Package:com.codingnomads.ioc.examples.setterinjection
Again, create a new POJO that represents a laptop component.
In the LaptopConfiguration class, declare a bean that returns an instance of this new class. This time, in the Laptop class, add a field for your new class and add an @Autowired setter - it will now be injected!
Please be sure to push your work to GitHub when you're done.
Field Injection
Field-based injection is by far the most compact version of DI. You do not need constructors or setters. No, all you need is a field and an @Autowired used like so:
public class FieldInjectionExample {
@Autowired
private Dependency dependency;
}
Again, this example is effectively identical to those above, other than that it uses field injection to wire the dependency into the FieldInjectionExample. Nice and succinct.
Which Dependency Injection Method to Choose
You may instinctually gravitate towards the less verbose version of DI like field DI, but there are downsides to the low word count. The most significant and most relevant being the fact that constructor DI forces the injection of the required dependencies while setter and field DI do not.
When you think about it, this is really just an OOP concept - ensuring the inclusion of elements in an object by placing them in all constructors. This principal extends to DI as well - for a nearly foolproof guarantee that your dependency is present, opt for constructor DI; otherwise, choose the DI type that suits your preferences.
Fortunately, Spring's seamless handling of dependency injection typically eliminates the need for explicit guarantees in most cases. So, sit back and use whichever DI method that makes you feel at ease – Spring has got your back!
Tip: In enterprise settings, constructor injection is frequently preferred, emphasizing the importance of explicit dependency initialization during object creation. While there's often flexibility in choosing the type of dependency injection you prefer, constructor injection aligns closely with best practices in enterprise-level software development.
Summary: Spring Dependency Injection
This lesson covered how to utilize dependency injection in Spring. You don't need to fully grasp all the subtleties of DI right away. However, it's such an essential part of Spring that as you go through the examples and exercises in the coming lessons, it will soon become second nature. As you move forward, you'll get more comfortable with topics like:
- IoC & Dependency Injection Overall
- Constructor Injection: inject a Bean into a class using a Java constructor
- Setter Injection: inject a Bean into a class using a setter method and an
@Autowired - Field Injection: inject a Bean into a class by declaring the field needed and annotating it with
@Autowired
Keep up the good work!