In Spring, Dependency Injection (DI) means you let the Spring container create and provide the objects your class depends on, instead of manually creating them with new.
The recommended approach in modern Spring is constructor injection.
1. Define a dependency as a Spring bean
import org.springframework.stereotype.Component;
@Component
public class EmailSender {
public void send(String message) {
System.out.println("Sending email: " + message);
}
}
@Component tells Spring: “Create and manage an instance of this class.”
2. Inject it into another bean
import org.springframework.stereotype.Service;
@Service
public class NotificationService {
private final EmailSender emailSender;
public NotificationService(EmailSender emailSender) {
this.emailSender = emailSender;
}
public void notifyUser(String message) {
emailSender.send(message);
}
}
Because NotificationService is also a Spring bean, Spring sees its constructor and automatically provides an EmailSender.
In modern Spring, if there is only one constructor, you usually do not need @Autowired on the constructor.
3. Use the service from a controller or another bean
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class NotificationController {
private final NotificationService notificationService;
public NotificationController(NotificationService notificationService) {
this.notificationService = notificationService;
}
@GetMapping("/notify")
public String notifyUser() {
notificationService.notifyUser("Hello from Spring!");
return "Notification sent";
}
}
Common DI styles in Spring
Constructor injection — recommended
@Service
public class MyService {
private final MyDependency myDependency;
public MyService(MyDependency myDependency) {
this.myDependency = myDependency;
}
}
Use this most of the time because it:
- makes dependencies explicit
- supports immutability with
final - is easier to test
- avoids partially initialized objects
Setter injection
@Service
public class MyService {
private MyDependency myDependency;
@Autowired
public void setMyDependency(MyDependency myDependency) {
this.myDependency = myDependency;
}
}
Use setter injection mainly for optional dependencies or dependencies that may change after construction.
Field injection — usually avoid
@Service
public class MyService {
@Autowired
private MyDependency myDependency;
}
This works, but it is generally discouraged because it makes testing harder and hides the class’s required dependencies.
Injecting interfaces
A common pattern is to inject an interface rather than a concrete class:
public interface MessageSender {
void send(String message);
}
import org.springframework.stereotype.Component;
@Component
public class EmailSender implements MessageSender {
@Override
public void send(String message) {
System.out.println("Email: " + message);
}
}
import org.springframework.stereotype.Service;
@Service
public class NotificationService {
private final MessageSender messageSender;
public NotificationService(MessageSender messageSender) {
this.messageSender = messageSender;
}
public void notifyUser(String message) {
messageSender.send(message);
}
}
If there is only one implementation of MessageSender, Spring injects it automatically.
When there are multiple implementations
If multiple beans match the same type, use @Qualifier:
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class NotificationService {
private final MessageSender messageSender;
public NotificationService(@Qualifier("emailSender") MessageSender messageSender) {
this.messageSender = messageSender;
}
}
The qualifier value usually matches the bean name, which by default is the class name with a lowercase-first letter.
Creating beans with @Bean
You can also define beans in a configuration class:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public EmailSender emailSender() {
return new EmailSender();
}
}
This is useful when:
- the class comes from a third-party library
- construction requires custom setup
- you do not want to annotate the class with
@Component
Summary
Use this pattern most of the time:
@Service
public class MyService {
private final MyDependency myDependency;
public MyService(MyDependency myDependency) {
this.myDependency = myDependency;
}
public void doWork() {
myDependency.doSomething();
}
}
And make sure the dependency is a Spring bean:
@Component
public class MyDependency {
public void doSomething() {
System.out.println("Dependency logic executed.");
}
}
In short: annotate your classes with Spring stereotypes like @Component, @Service, or @Repository, then inject dependencies through constructors.
