Dependency Injection
October 3, 2024
Dependency Injection
The Spring Application Context
The best explanation of the Spring Application Context that I have found is at: What is Spring Framework? An Unorthodox Guide
The Spring Application Context
- It creates and configures the beans according to the metadata.
- It manages the lifecycle of the beans, handling their creation, initialization, and destruction.
- It provides access to the beans through various methods like getBean().
Spring Beans
A Spring Bean is essentially an object whose creation, proliferation and destruction is left to the Spring Framework. Not every bean in Spring is a singleton. While singleton is the default scope for beans in Spring, there are other scopes available:
Prototype: A new bean instance is created each time it is requested.
Request: A new bean instance is created for each HTTP request (in web applications).
Session: A new bean instance is created for each HTTP session (in web applications).
Application: A new bean instance is created for each ServletContext (in web applications).
Custom: It is possible to define a custom scope.
Thread: A new bean instance will be created by Spring when requested by a new thread, while for the same thread, the same bean instance will be returned. Note that this scope is not registered by default.
WebSocket: A new bean instance is created for each WebSocket session (in WebSocket applications).
Stereotype Annotations:
@Component: A generic stereotype annotation for any Spring-managed component.
@Service: Indicates a class that provides a business service.
@Repository: Indicates a class that provides data access operations (e.g., a DAO class).
@Controller: Indicates a class that handles web requests in Spring MVC applications.
@RestController: A specialized version of @Controller for creating RESTful web services.
Configuration Annotations:
@Configuration: Marks a class as a source of bean definitions.
@Bean: Used on methods within a @Configuration class to define a bean.
What's the difference between the Stereotype and Configuration Annotations?
Bean Definition:
Stereotype Annotations: Implicitly defined through component scanning. Configuration Annotations: Explicitly defined using @Bean methods.
Configuration Style:
Stereotype Annotations: Declarative, where annotations are placed on classes.
Configuration Annotations: Programmatic, using Java code.
Flexibility:
Stereotype Annotations: Offers less flexibility as it is limited to component scanning.
Configuration Annotations: Provides more flexibility, allowing for conditional logic.
Use Cases:
Stereotype Annotations: Best suited for simple components with well-defined roles.
Configuration Annotations: Ideal for more complex bean configurations or scenarios requiring conditional logic.
Injection Methods
Constructor Injection
import org.springframework.stereotype.Service;
@Service
public class GreetingService {
private final MessageProvider messageProvider;
public GreetingService(MessageProvider messageProvider) {
this.messageProvider = messageProvider;
}
public String getGreeting() {
return messageProvider.getMessage() + " from Spring Boot!";
}
}
@Service
public class MessageProvider {
public String getMessage() {
return "Hello";
}
}
Setter Injection
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class GreetingService {
private MessageProvider messageProvider;
@Autowired
public void setMessageProvider(MessageProvider messageProvider) {
this.messageProvider = messageProvider;
}
public String getGreeting() {
return messageProvider.getMessage() + " from Spring Boot!";
}
}
@Service
public class MessageProvider {
public String getMessage() {
return "Hello";
}
}
Field Injection
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class GreetingService {
@Autowired
private MessageProvider messageProvider;
public String getGreeting() {
return messageProvider.getMessage() + " from Spring Boot!";
}
}
@Service
public class MessageProvider {
public String getMessage() {
return "Hello";
}
}
Note that the authors of Pro Spring 6 advise against the use of field injection for the following reasons
- Increases difficulty of refactoring the class because it can lead to bloat.
- It can hide dependencies.
- The bean is no longer a POJO and can't be instantiated without Spring.
Injecting Values
application.properties file in your src/main/resources directory has the following content:
app:
greeting: "Hello from config"
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;
@Service
public class GreetingService {
@Value("${app.greeting}")
private String greetingMessage;
public String getGreeting() {
return greetingMessage;
}
}
Injecting Collections
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
@Service
public class GreetingAggregator {
@Autowired
private List<GreetingProvider> greetingProviders;
public List<String> getAllGreetings() {
return greetingProviders.stream()
.map(GreetingProvider::getGreeting)
.toList();
}
}
public interface GreetingProvider {
String getGreeting();
}
@Service
public class EnglishGreetingProvider implements GreetingProvider {
public String getGreeting() {
return "Hello";
}
}
@Service
public class FrenchGreetingProvider implements GreetingProvider {
public String getGreeting() {
return "Bonjour";
}
}
Method Injection
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class GreetingService {
private String message;
@Autowired
public void prepareMessage(MessageProvider messageProvider) {
this.message = messageProvider.getMessage();
}
public String getGreeting() {
return message + " from Spring Boot!";
}
}
@Service
public class MessageProvider {
public String getMessage() {
return "Hello";
}
}
Autowiring
Autowiring is the process of implicitly injecting beans into other beans that depend on them. Spring supports four modes of autowiring:
-
byName: In this mode, Spring wires each property by matching its name with a bean name in the ApplicationContext. For example, if the target bean has a property named foo, and a bean named foo is defined in the ApplicationContext, then this foo bean will be injected into the foo property of the target bean.
-
byType: With byType autowiring, Spring tries to wire each property of the target bean using a bean of the same type from the ApplicationContext. If a matching type is found, it is injected into the corresponding property.
-
constructor: This mode works similarly to byType, but instead of using setter methods, Spring uses the target bean's constructor to perform injection. Spring tries to match the constructor that can be fully satisfied by the beans available in the ApplicationContext. For instance, if your bean has two constructors, one that takes a String and another that takes both a String and an Integer, and matching beans are found, Spring will use the constructor with both parameters.
-
default: In default mode, Spring chooses between constructor and byType modes automatically. If your bean has a no-argument constructor, Spring will use byType; otherwise, it will fall back to constructor.