🎉 Special Offer !    Code: GET300OFF    Flat ₹300 OFF on every Java Course
Grab Deal 🚀

Dependency Injection (DI) Example in Spring


Lets create 2 programs of Dependency Injection (DI), first in Core Java and second in Spring and then compare both of them.

Core Java Dependency Injection (DI) Example

  • In core Java, DI is achieved manually by passing dependencies through the constructor or setter methods.

  • Program
    Below is the program demonstrating Dependency Injection in Core Java.
    • "Address" class which is created with city and state attributes.
      Address.java
      package in.sp.beans;
      
      public class Address
      {
          private String city;
          private String state;
          
          // Constructor
          public Address(String city, String state)
          {
              this.city = city;
              this.state = state;
          }
          
          // Getters
          public String getCity() 
          {
              return city;
          }
          
          public String getState() 
          {
              return state;
          }
          
          @Override
          public String toString()
          {
              return city + ", " + state;
          }
      }
    • "Student" class which is constructed with a dependency on Address.
      Student.java
      package in.sp.beans;
      
      public class Student 
      {
          private String name;
          private Address address;  // Dependency
          
          // Constructor Injection
          public Student(String name, Address address) 
          {
              this.name = name;
              this.address = address;
          }
          
          // Getter methods
          public String getName()
          {
              return name;
          }
          
          public Address getAddress()
          {
              return address;
          }
          
          @Override
          public String toString() 
          {
              return "Student Name: " + name + ", Address: " + address;
          }
      }
    • "MainApp" class in which an Address instance is created and passed into the Student constructor, achieving manual dependency injection.
      MainApp.java
      package in.sp.main;
      
      import in.sp.beans.Address;
      import in.sp.beans.Student;
      
      public class MainApp 
      {
          public static void main(String[] args)
          {
              // Manually creating Address and injecting into Student
              Address address = new Address("Panchkula", "Haryana");
              Student student = new Student("Deepak", address);
              
              System.out.println(student);
          }
      }
    • Below is the output
      Output:
      Student Name: Deepak, Address: Panchkula, Haryana


  • Disadvantages of Above Program
    Manual dependency injection, as shown in above example, has several disadvantages, especially when compared to frameworks like Spring that handle dependency injection automatically.
    1. Tight Coupling: We create instances of dependent objects (Address) directly in the main class (MainApp). This hardcodes specific implementations into our class, making it difficult to swap dependencies (like a different Address implementation) without modifying the code.
    2. Difficult to Test: Manual injection makes testing harder. We cannot easily replace dependencies with mock objects or test variations of dependencies without modifying the MainApp code. With automatic dependency injection, we can more easily pass mock objects for testing.
    3. Hard to Maintain and Scale: As the number of dependencies grows, we have to manually create and inject each one, making the codebase more complex and harder to maintain. Adding new dependencies or changing existing ones means editing multiple parts of the code.
    4. Lack of Flexibility: Manual injection limits flexibility when managing dependency lifecycles, like singleton versus prototype scopes, as we must handle these lifecycle decisions manually.
    5. No Centralized Control: Dependency injection frameworks (like Spring) provide a centralized container to manage dependencies. With manual injection, there’s no such container, making it harder to track and control all dependencies throughout the application.
    6. Reduced Reusability: The Student class is less reusable because it depends on Address being provided directly. If we want to use Student in another context with different dependencies, we have to modify or recreate Student each time.

Now, let's create a Dependency Injection (DI) program in Spring that will eliminate all the disadvantages of the Core Java Dependency Injection program mentioned above.

Spring Dependency Injection (DI) Example

  • In Spring, DI is achieved by Spring IoC container, which injects dependencies defined in the configuration file or through annotations.

  • NOTE : Below program is an example of Automatic Dependency Injection (known as Autowiring), not Manual Dependency Injection.
    For Manual Dependency Injection Program Click Here

  • Program
    Below is the program demonstrating Dependency Injection in Spring.
    • "Address" class with @Component annotation.
      Address.java
      package in.sp.beans;
      
      import org.springframework.stereotype.Component;
      
      @Component  // Marks this class as a Spring-managed bean
      public class Address 
      {
      	private String city = "Pune";
      	private String state = "Maharashtra";
       
      	// Constructor
      	public Address() {}
       
      	// Getters
      	public String getCity()
      	{
      		return city;
      	}
       
      	public String getState()
      	{
      		return state;
      	}
       
      	@Override
      	public String toString() 
      	{
      		return city + ", " + state;
      	}
      }
    • "Student" class annotated with @Component and used @Autowired for dependency on Address.
      Student.java
      package in.sp.beans;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Component;
      
      @Component  // Marks this class as a Spring-managed bean
      public class Student
      {
      	private String name = "Deepak";
       
      	private Address address;  // Dependency
       
      	// Constructor Injection
      	@Autowired  // Indicates that Spring should inject Address here
      	public Student(Address address)
      	{
      		this.address = address;
      	}
       
      	// Getter methods
      	public String getName() 
      	{
      		return name;
      	}
       
      	public Address getAddress() 
      	{
      		return address;
      	}
       
      	@Override
      	public String toString() 
      	{
      		return "Student Name: " + name + ", Address: " + address;
      	}
      }
    • "AppConfig" class which is configuration class and here we will scan the package.
      AppConfig.java
      package in.sp.config;
      
      import org.springframework.context.annotation.ComponentScan;
      import org.springframework.context.annotation.Configuration;
      
      @Configuration
      @ComponentScan(basePackages = "in.sp.beans")
      public class AppConfig
      {
      	// No explicit bean definitions needed; @Component classes will be auto-detected
      }
      
    • "MainApp" class in which we will start the container and execute the program.
      MainApp.java
      package in.sp.main;
      
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.annotation.AnnotationConfigApplicationContext;
      
      import in.sp.beans.Student;
      import in.sp.config.AppConfig;
      
      public class MainApp
      {
      	public static void main(String[] args) 
      	{
      		// Loading Spring context from annotations
      		ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
      		
      		// Retrieving the Student bean, which has Address injected
      		Student student = context.getBean(Student.class);
      		
      		System.out.println(student);
      	}
      }
    • Below is the output
      Output:
      Student Name: Deepak, Address: Pune, Maharashtra


  • In Spring DI program we didnt provide any manual dependency, spring automatically deducts and injects the dependency.
  • So this example demonstrates how Spring's DI with annotations simplifies object creation and dependency management compared to manual injection in core Java.