Spring Boot Tutorial

Spring Boot - Software Setup and Configuration (STS/Eclipse/IntelliJ)

Prerequisite (Spring Core Concepts)

Spring Boot Core

Spring Boot with REST API

Spring Boot with Database and Data JPA

Spring Boot with Kafka

Spring Boot with AOP

Spring - Dependency Injection by Setter Method

Dependency Injection (DI) is a core concept in Spring, allowing you to inject dependencies into Spring beans. The Spring container is responsible for the creation, initialization, and assembly of beans. There are multiple ways to inject dependencies in Spring, one of which is setter-based injection.

Here's a step-by-step guide to perform Dependency Injection via setter methods in a traditional Spring application (non-Spring Boot):

1. Create the Beans

First, let's define two simple classes, Author and Book.

public class Author {
    private String name;

    // Getter and setter methods
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class Book {
    private String title;
    private Author author;

    // Getter and setter methods for both properties
    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Author getAuthor() {
        return author;
    }

    public void setAuthor(Author author) {
        this.author = author;
    }
}

2. Create the Spring Configuration

In a Spring XML configuration file (e.g., beans.xml):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- Definition for author bean -->
    <bean id="authorBean" class="com.example.Author">
        <property name="name" value="John Doe"/>
    </bean>

    <!-- Definition for book bean -->
    <bean id="bookBean" class="com.example.Book">
        <property name="title" value="Spring in Action"/>
        <property name="author" ref="authorBean"/>  <!-- Setter-based DI here -->
    </bean>

</beans>

Notice the use of the <property> tag which defines the property of the bean to inject.

3. Test the Configuration

To validate the configuration and ensure that the injection works as expected, write a simple test:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        
        Book book = (Book) context.getBean("bookBean");
        
        System.out.println("Book Title: " + book.getTitle());
        System.out.println("Author: " + book.getAuthor().getName());
    }
}

Running the above App class should display the book title and author name, proving that the dependencies were injected properly.

Points to Remember:

  • Setter-based DI is realized by calling setter methods on your beans after invoking a no-argument constructor to instantiate the bean.

  • Constructor-based DI is another common way to inject dependencies using the bean's constructor.

  • It's a best practice not to mix both setter-based and constructor-based DI for the same bean. If you do mix them, the constructor injection happens first.

Spring's DI helps decouple the configuration and specification of application objects from the actual program logic, allowing for cleaner and more modular code.

  1. Setter-based dependency injection in Spring:

    • Description: In Spring, setter-based dependency injection involves providing dependencies to a class through setter methods. This is one of the ways to achieve Inversion of Control (IoC) in the Spring framework.
    • Code:
      public class MyService {
          private MyRepository myRepository;
      
          public void setMyRepository(MyRepository myRepository) {
              this.myRepository = myRepository;
          }
      
          // Other methods using myRepository
      }
      
  2. Configuring beans and dependencies using setters in Spring:

    • Description: Configure beans and their dependencies in the Spring context XML or JavaConfig. Set the dependencies through setters during the bean configuration.

    • Code:

      <!-- XML Configuration -->
      <bean id="myService" class="com.example.MyService">
          <property name="myRepository" ref="myRepository"/>
      </bean>
      
      // JavaConfig Configuration
      @Configuration
      public class AppConfig {
          @Bean
          public MyService myService() {
              MyService service = new MyService();
              service.setMyRepository(myRepository());
              return service;
          }
      
          @Bean
          public MyRepository myRepository() {
              return new MyRepository();
          }
      }
      
  3. Optional and required dependencies in setter injection with Spring:

    • Description: Mark dependencies as optional or required based on the business logic. Required dependencies are injected by default unless specified otherwise.
    • Code:
      public class MyService {
          private MyRepository myRepository;
      
          // Required dependency
          public void setMyRepository(MyRepository myRepository) {
              this.myRepository = myRepository;
          }
      
          // Optional dependency
          public void setOptionalDependency(MyOptionalService optionalService) {
              // Logic to handle optional dependency
          }
      }
      
  4. Qualifiers and named dependencies in setter injection with Spring:

    • Description: Use @Qualifier annotation to specify which bean to inject when there are multiple candidates. Alternatively, provide a name to the setter method.

    • Code:

      public class MyService {
          private MyRepository myRepository;
      
          @Autowired
          @Qualifier("specificRepository")
          public void setMyRepository(MyRepository myRepository) {
              this.myRepository = myRepository;
          }
      }
      
      public class MyService {
          private MyRepository myRepository;
      
          public void setSpecificRepository(MyRepository myRepository) {
              this.myRepository = myRepository;
          }
      }
      
  5. Using @Value annotation with setter injection in Spring:

    • Description: Inject values from properties files, system properties, or environment variables using the @Value annotation with setter methods.
    • Code:
      public class MyService {
          private String apiKey;
      
          @Value("${api.key}")
          public void setApiKey(String apiKey) {
              this.apiKey = apiKey;
          }
      }
      
  6. Testing and mocking dependencies in Spring with setter injection:

    • Description: During testing, use setters to inject mock or test dependencies into Spring beans, facilitating easy testing and isolation.
    • Code:
      public class MyServiceTest {
          @Test
          public void testServiceLogic() {
              MyService myService = new MyService();
              MyRepository mockRepository = Mockito.mock(MyRepository.class);
              myService.setMyRepository(mockRepository);
      
              // Test service logic
          }
      }
      
  7. Setter injection vs. constructor injection in the Spring framework:

    • Description: Choose between setter injection and constructor injection based on your design preferences and requirements. Constructor injection is often preferred for mandatory dependencies, while setter injection allows for flexibility.

    • Code: (Comparison)

      // Setter Injection
      public class MyService {
          private MyRepository myRepository;
      
          public void setMyRepository(MyRepository myRepository) {
              this.myRepository = myRepository;
          }
      }
      
      // Constructor Injection
      public class MyService {
          private final MyRepository myRepository;
      
          public MyService(MyRepository myRepository) {
              this.myRepository = myRepository;
          }
      }