What is Spring Dependency Injection(DI)?
- It is the fundamental aspect of the spring framework through which the spring container injects the properties of one object into another object.
- Every object will have its dependencies or basic requirements.
- Dependency injection also ensures loose coupling between classes.
Advantages of Dependency Injection
-
Loose Coupling:
-
- Dependency Injection reduces coupling between components(Objects). Classes are not directly responsible for creating their dependencies.
-
Increased Testability:
-
- DI makes it easier to test individual components in isolation.
-
Code Reusability:
-
- After injecting dependencies, classes become more modular and can be reused in different contexts.
-
Easy Debugging:
-
- Since dependencies are injected from external resources, tracing and debugging issues becomes easier.
-
Promotes Separation of Concerns:
-
- Dependency Injection promotes the separation of concerns by creating and managing dependencies without disturbing business logic.
Types of Dependency Injection.
Spring framework provides two options to inject the dependencies.
- Constructor Injection
- Setter Injection
Constructor Injection:
- In this case, the container will invoke the constructor with an argument representing the dependency we want to set.
- We can inject multiple dependencies through the constructor.
- To inject dependencies through the constructor we have to configure all the details inside the bean configuration file.
Setter Injection:
- In the case of setter injection, you use the setter method to inject dependencies.
- You can use setter injection to inject partial dependencies of an object.
- Setter injection is more flexible than constructor injection.
Dependency Injection Configuration
Similar to Spring IoC there are two ways to configure dependencies.
- XML-based configuration
- Annotation-based configuration
XML-based configuration
Let’s discuss how to inject dependency through an XML file.
package banking; //MAIN ENTITY public class Atm { //declaration of dependency private Printer print; //constructor injection public Atm(Printer print) { this.print = print; } //access dependency properties public void printBalanceInformation(int accNo) { print.printInfo(accNo); } }
package banking; //DEPENDENCY CLASS public class Printer { public void printInfo(int accountNo) { System.out.println("PRINTING BALANCE INFO FOR "+accountNo); System.out.println("ACCOUNT BALANCE IS 25000"); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- Define your beans here --> <!-- CONFIGURE OBJECTS FOR DEPENDENCIES --> <bean id="printerdependency" class="banking.Printer"></bean> <bean id="atmentity" class="banking.Atm"> <constructor-arg ref="printerdependency" /> </bean> </beans>
In this example:
Printer
class has one method printInfo() which accepts account no and prints the information.
Atm
the class has a constructor that accepts Printer
as a parameter and the method printBalanceInformation() prints the balance information by using the reference of the injected Printer reference.
In the XML file Printer object is injected into an ATM through the constructor.
Annotation-based configuration
Following is the process of injecting dependencies by using Annotation
package banking; //MAIN ENTITY @Component public class Atm { //declaration of dependency private Printer print; //constructor injection @Autowired public Atm(Printer print) { this.print = print; } //access dependency properties public void printBalanceInformation(int accNo) { print.printInfo(accNo); } }
package banking; //DEPENDENCY CLASS @Component public class Printer { public void printInfo(int accountNo) { System.out.println("PRINTING BALANCE INFO FOR "+accountNo); System.out.println("ACCOUNT BALANCE IS 25000"); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- Enable Component Scanning for java classes --> <context:component-scan base-package="banking"></context:component-scan> </beans>
The @component
annotation is used to indicate that the Atm
class is a Spring component that should be automatically discovered and registered during the component scanning process.
The @Autowired
annotation on the constructor indicates that the Printer
dependency should be injected by Spring.
Conclusion
This example demonstrates how constructor injection helps in achieving loose coupling between the `Atm` and `Printer` classes. The `Atm` does not create its own `Printer` instance; instead, it injects `Printer` externally. It makes it easier to replace or modify the `Printer` without affecting the `Atm` class.
Working of @Autowired
annotation
- Spring container identifies the target dependency based on type.
- If there is a single matching bean of the required type then it is automatically injected.
- If there are multiple dependencies then Spring tries to resolve the ambiguity if it can’t then throws an exception.
- To resolve multiple dependencies we can use
@Qualifier
annotation where we can specify the name of the dependency. - The injection process occurs during the initialization of the Spring container or when a bean is created.