This tutorial discusses Dependency Injection (DI) in
Spring Boot and how DI can be achieved. But before that, we will know what is
Inversion of Control (IoC) and the relation between the Inversion of
Control and Dependency Injection (DI).
1. What is Inversion of Control (IoC)
Inversion of Control (Ioc) is a Design Principle that is often used in the
Object Oriented Programming (OOP) context. The principle of IoC is that the
interdependency between/among the classes should be minimal - which means
we can easily plug or unplug any dependent entities without affecting the
actual control flow (which means the classes should be loosely coupled). To
understand this concept let’s consider the following examples:
Example #1
public class MySQLDb {
public String connect(String connectionURL) {
System.out.println(“Connection URL :: ” + connectionURL);
}
}
public class MyApplication {
MySQLDb dbConnection = new JavaDeveloper();
dbConnection.connect(“db:mysql”)
}
We have created a MySQLDb class with the connect() procedure. In the
MyApplication, we create an instance of MySQLDb and call
connect().
Example #2
public interface IDbConnection {
public String connect(String connectionURL);
}
public class MySQLDb implements IDbConnection {
@override
public String connect(String connectionURL) {
System.out.println(“Connection URL :: ” + connectionURL);
}
}
public class OracleDb implements IDbConnection {
@override
public String connect(String connectionURL) {
System.out.println(“Connection URL :: ” + connectionURL);
}
}
public class MyApplication {
IDbConnection dbConnection = new MySQLDb();
dbConnection.connect(“db:mysql”);
dbConnection = new OracleDb();
dbConnection.connect(“db:oracle”);
}
Here MySQLDb and OracleDb implement the IDbConnection interface - and each class overrides the
connect() procedure in their way. In MyApplication, we have
created instances of MySQLDb and OracleDb and assigned them to
the generic IDbConnection interface.
Observations
Example #1
- MyApplication can only use MySQLDb. So these classes are tightly coupled - which means dependency between them is in higher order.
- If MyApplication wants to connect to any other database, we need to change the entire code.
- Here MyApplication entirely controls the flow of MySQLDb dependency.
Example #2
- We can easily plug any type of database connectivity (MySQL DB or OracleDB) into MyApplication. So these classes are loosely coupled - which means dependency between them is low.
- As we can pass the connectionURL, the database server can easily be changed.
- Here MyApplication does not entirely control the flow of MySQLDb/OracleDb dependency - the control is now to the user of the MyApplication.
So, with Example #1, users or any external entity can not change the
control flow of MyApplication. On the other hand, users or external
entities can modify the control flow of MyApplication in
Example #2 - the control flow of MyApplication is
inverted, which means an external entity or service or any framework
can control the flow as well as the dependencies. This is called
Inversion of Control (IoC).
A perfect example of the implementation of Inversion of Control (IoC) is the
Spring Framework. The org.springframework.beans and
org.springframework.context packages are the basis for Spring
Framework's IoC container. The BeanFactory is a primary
component of the core IoC container and the ApplicationContext adds
more enterprise-specific functionality. Objects are the backbone of any Spring
Application. In Spring Framework, they are managed by the IoC Containers -
they are called beans. So, simply a bean is one of the many objects in
our application.
Inversion of Control (IoC) can be achieved in various ways, such as
Strategy design pattern, Service Locator pattern,
Factory pattern, and Dependency Injection (DI).
2. What is Dependency Injection (DI)
Dependency Injection (DI) is the implementation of IoC. So without
IoC principle, Dependency Injection is not possible. Let’s understand the
dependency injection with this example: suppose object Car requires a
method of object Engine to initiate the engine start process. So,
dependency injection suggests that instead of creating an instance of class
Engine in class Car using the new operator, the object of
class Engine should be injected in class Car.
Just take a look at the following code snippet for a clear understanding:
public class Engine {
public void startEngine() {
System.out.println(“Initial engine start process…”);
}
}
public class Car {
private Engine engine;
public Car(Engine engine) {
This.engine = engine;
}
}
So, in dependency injection object creation is done by the Spring IoC
containers rather than the application itself. It reduces coupling between
multiple objects as it is dynamically injected by the Spring Framework.
In summary:
Spring Framework has implemented Inversion Of Control (IoC) via IoC
Container. And IoC Container helps to achieve Dependency Injection (DI).
3. Injection Type
Spring framework mainly supports two forms of Dependency Injection:- Constructor Injection: In the constructor-based dependency injection, we inject the dependent objects through its constructor argument at the time of initializing it. This way we can declare all dependencies in one step. This is the best practice to inject the dependencies.
- Setter Injection: In setter injection, dependencies are injected via the setter methods. The IoC container will use these setter methods to allocate the dependencies at run-time after invoking a no-argument constructor or no-argument static factory method to instantiate their bean.
In this tutorial, we will examine annotation-based Dependency Injection
and use @Autowired annotation to implement DI in Spring
Boot.
First, we will implement the
constructor-based dependency injection and then look into the
setter-based dependency injection.
4. Spring Boot project creation
To create a new Spring Boot project, we will use Spring Initializr (https://start.spring.io/), generating a basic structure for our Spring Boot project. We
have added the following dependencies:
- Spring Boot DevTools - necessary development tools
Then click on GENERATE to download the project zip file. Unzip the
zip file. Now import the project in Eclipse/STS as a Maven project.
5. Constructor Injection
For constructor injection, the IoC container will invoke the parameterized
constructor of the class to inject dependencies.
To implement this injection, we use the relationship between Car and
Engine as our example to this DI - cars are dependent on
engines, so inject the engine dependency in the car.
We have created an interface IEngineService which contains a method
getEngineDetails():
public interface IEngineService {
public String getEngineDetails();
}
This IEngineService interface has been implemented by the EngineServiceImpl class:
@Component
public class EngineServiceImpl implements IEngineService{
@Override
public String getEngineDetails() {
return "All alloy quad overhead cam, 4.0 litre twin turbo V8.";
}
}
So, EngineServiceImpl overrides the getEngineDetails() method and
this method returns a description of the engine. This class is annotated
with @Component annotation. By this annotation, we are making
EngineServiceImpl a custom bean and EngineServiceImpl is now eligible to be
automatically detected by the Spring IoC container. Spring will initiate it and inject it whenever needed. So this bean will be managed by IoC container.
We have another interface ICar which contains two methods:
getCarDetails() and getEngine().
public interface ICar {
public String getCarDetails();
public String getEngine();
}
The ICar interface is implemented by the AstonMartin class:
@Component
public class AstonMartin implements ICar {
private IEngineService engine;
@Autowired
public AstonMartin(IEngineService engine) {
this.engine = engine;
}
@Override
public String getCarDetails() {
return "V8 Roadster";
}
@Override
public String getEngine() {
return this.engine.getEngineDetails();
}
}
Here we have declared a private field for the IEngineService, and
used it inside the getEngine() method to getEngineDetails().
In the AstonMartin class constructor, we passed
IEngineService as an argument and assigned it to the private field.
We have also annotated the constructor with @Autowired annotation.
@Autowired annotation allows IoC containers to resolve
dependencies automatically by injecting the beans that have been defined.
As we are wiring the constructor, Spring Framework will inject the beans with the
same type as the constructor arguments. This is Constructor
Injection.
Now Spring will scan for the component that implements
IEngineService interface, in this case, it is
EngineServiceImpl. Spring will create an instance of
EngineServiceImpl and inject it into AstonMartin.
In the main method, we are acquiring the bean (AstonMartin) from the
ApplicationContext and calling the methods: getCarDetails()
and getEngine().
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringbootdiApplication.class, args);
System.out.println("\nApplication started...");
System.out.println("\n*** Beans ****");
System.out.println("\nNo of Beans :: " + context.getBeanDefinitionCount());
System.out.println("\nBean names ::");
int count = 0;
for (String s : context.getBeanDefinitionNames()) {
System.out.println("Name #" + (count + 1) + " : " + s);
count++;
}
System.out.println();
// GET BEAN FROM SPRING CONTAINER
ICar astonMartin = context.getBean(AstonMartin.class);
// CALL A METHOD ON THE BEAN
System.out.println(astonMartin.getCarDetails());
// CALL METHOD TO GET ENGINE DETAILS
System.out.println(astonMartin.getEngine());
}
Now run the application and inspect the audit log. Along with the many beans,
which are automatically loaded by the Spring Framework, we can see our custom beans:
astonMartin and engineServiceImpl.
And also the outputs of the bean methods:
So this is all about Constructor Injection.
6. Setter Injection
In this case, the IoC container will invoke the setter methods of the
class to inject required dependencies.
To implement this DI, we will use the previous example of Car and Engine
and will inject IEngineService through the setter method. Let's
do it.
So we have modified the AstonMartin class - the only difference
is that we have wired a setter method, setEngineService(), to
inject dependency.
@Component
public class AstonMartin implements ICar {
private IEngineService engine;
// @Autowired
// public AstonMartin(IEngineService engine) {
// this.engine = engine;
// }
@Autowired
public void setEngineService(IEngineService engine) {
this.engine = engine;
}
@Override
public String getCarDetails() {
return "V8 Roadster";
}
@Override
public String getEngine() {
return this.engine.getEngineDetails();
}
}
As we are auto-wiring a setter method, Spring will inject the beans with the
same type as the method arguments. This is all about Setter Injection.
7. Conclusion
So, we have learned how Spring Boot manages Inversion of Control and the
procedures by which we can achieve Dependency Injection in Spring Boot.
You can download the
source code.
Happy coding!!! 😊
No comments:
Post a Comment