Spring Dependency Injection

Loading

Dependency Injection (DI) is a core concept in the Spring Framework that promotes loose coupling and testability by injecting dependencies into a class rather than having the class create them itself. Spring provides several ways to implement Dependency Injection, including constructor injection, setter injection, and field injection.


1. Types of Dependency Injection

Spring supports three main types of dependency injection:

a. Constructor Injection

  • Dependencies are injected via the constructor.
  • Recommended for mandatory dependencies.

Example: Constructor Injection

public class NotificationService {
    private final MessageService messageService;

    // Constructor Injection
    public NotificationService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void notifyUser(String message) {
        messageService.sendMessage(message);
    }
}

b. Setter Injection

  • Dependencies are injected via setter methods.
  • Suitable for optional dependencies.

Example: Setter Injection

public class NotificationService {
    private MessageService messageService;

    // Setter Injection
    public void setMessageService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void notifyUser(String message) {
        messageService.sendMessage(message);
    }
}

c. Field Injection

  • Dependencies are injected directly into fields using annotations.
  • Less recommended due to reduced testability.

Example: Field Injection

import org.springframework.beans.factory.annotation.Autowired;

public class NotificationService {
    @Autowired
    private MessageService messageService;

    public void notifyUser(String message) {
        messageService.sendMessage(message);
    }
}

2. Configuring Dependency Injection

Spring provides two ways to configure dependency injection:

  1. XML Configuration.
  2. Annotation-Based Configuration.

a. XML Configuration

Define beans and their dependencies in an XML file.

Example: XML Configuration

<beans>
    <bean id="emailService" class="com.example.EmailService" />
    <bean id="notificationService" class="com.example.NotificationService">
        <constructor-arg ref="emailService" />
    </bean>
</beans>

b. Annotation-Based Configuration

Use annotations like @Component, @Service, @Repository, and @Autowired to configure dependencies.

Example: Annotation-Based Configuration

import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;

@Service
public class NotificationService {
    private final MessageService messageService;

    @Autowired
    public NotificationService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void notifyUser(String message) {
        messageService.sendMessage(message);
    }
}

3. Spring Bean Scopes

Spring beans can have different scopes, which determine their lifecycle and visibility.

ScopeDescription
SingletonOne instance per Spring IoC container (default scope).
PrototypeA new instance is created every time the bean is requested.
RequestOne instance per HTTP request (web-aware context only).
SessionOne instance per HTTP session (web-aware context only).
ApplicationOne instance per ServletContext (web-aware context only).
WebSocketOne instance per WebSocket session (web-aware context only).

Example: Configuring Bean Scope

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("prototype")
public class PrototypeBean {
    // Bean logic
}

4. Autowiring

Spring can automatically wire dependencies using the @Autowired annotation.

Example: Autowiring

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class NotificationService {
    private final MessageService messageService;

    @Autowired
    public NotificationService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void notifyUser(String message) {
        messageService.sendMessage(message);
    }
}

5. Qualifiers

If multiple beans of the same type exist, use the @Qualifier annotation to specify which bean to inject.

Example: Using Qualifiers

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class NotificationService {
    private final MessageService messageService;

    @Autowired
    public NotificationService(@Qualifier("emailService") MessageService messageService) {
        this.messageService = messageService;
    }

    public void notifyUser(String message) {
        messageService.sendMessage(message);
    }
}

6. Java-Based Configuration

Use Java classes to configure beans and dependencies.

Example: Java-Based Configuration

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
    @Bean
    public MessageService emailService() {
        return new EmailService();
    }

    @Bean
    public NotificationService notificationService() {
        return new NotificationService(emailService());
    }
}

7. Best Practices

  • Prefer constructor injection for mandatory dependencies.
  • Use setter injection for optional dependencies.
  • Avoid field injection for better testability.
  • Use qualifiers to resolve ambiguity when multiple beans of the same type exist.
  • Leverage Java-based configuration for type-safe and refactorable code.

By mastering Spring Dependency Injection, you can build loosely coupled, maintainable, and testable applications!

Leave a Reply

Your email address will not be published. Required fields are marked *