📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.
✅ Some premium posts are free to read — no account needed. Follow me on Medium to stay updated and support my writing.
🎓 Top 10 Udemy Courses (Huge Discount): Explore My Udemy Courses — Learn through real-time, project-based development.
▶️ Subscribe to My YouTube Channel (172K+ subscribers): Java Guides on YouTube
If you’ve ever written Java code where one class depends on another — say, a UserService
that uses a UserRepository
— you’ve likely done something like this:
UserRepository repo = new UserRepository();
UserService service = new UserService(repo);
This is manual wiring. But when your application grows in size, you don’t want to manually create and manage every object — that’s where Dependency Injection (DI) comes in.
In this article, we’ll break down Dependency Injection in Spring in a beginner-friendly way — what it is, why it matters, and how Spring handles it with zero pain.
What Is Dependency Injection?
Dependency Injection is a design pattern that lets another class or framework provide an object’s dependencies, rather than the object creating them itself.
Instead of saying:
“I’ll build my own tools.”
You’re saying:
“Spring, please give me the tools I need.”
This promotes:
- Loose coupling
- Better testability
- Cleaner code
🌱 How Spring Implements DI
Spring’s Inversion of Control (IoC) container is responsible for managing your application’s components (called beans) and injecting dependencies automatically.
There are three main types of DI in Spring:

Let’s explore each one with simple examples.
✅ 1. Constructor Injection (Recommended)
This is the most preferred way of doing DI in Spring — it makes your beans immutable and easier to test.
Example:
@Service
public class UserService {
private final UserRepository userRepository;
// Constructor injection
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void registerUser(String name) {
userRepository.save(name);
}
}
@Repository
public class UserRepository {
public void save(String name) {
System.out.println("User saved: " + name);
}
}
Spring will automatically inject UserRepository
when creating UserService
.
✅ 2. Setter Injection
Useful when a dependency is optional, or when circular dependencies exist (though you should avoid those).
Example:
@Service
public class NotificationService {
private EmailService emailService;
@Autowired
public void setEmailService(EmailService emailService) {
this.emailService = emailService;
}
}
Setter injection gives you flexibility — but be careful not to introduce mutable state unintentionally.
⚠️ 3. Field Injection (Not Recommended for Production)
Quickest to write, but harder to test and violates immutability.
@Service
public class ProductService {
@Autowired
private ProductRepository repository;
public void listProducts() {
repository.findAll();
}
}
Use this for prototypes, tutorials, or test setups — but avoid it in production-grade code.
Annotation Overview

🏗️ Real-World Example: Email Notification System
Let’s build a basic flow:
NotificationService
depends onEmailService
- Spring will inject the dependency
Step 1: Create the Services
@Service
public class EmailService {
public void sendEmail(String email) {
System.out.println("Email sent to: " + email);
}
}
@Service
public class NotificationService {
private final EmailService emailService;
public NotificationService(EmailService emailService) {
this.emailService = emailService;
}
public void notifyUser(String email) {
emailService.sendEmail(email);
}
}
Step 2: Bootstrapping with Spring Boot
@SpringBootApplication
public class App {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(App.class, args);
NotificationService service = context.getBean(NotificationService.class);
service.notifyUser("test@example.com");
}
}
Output:
Email sent to: test@example.com
And just like that, you’ve implemented DI without writing a single new
keyword. 🎉
🔍 Why Constructor Injection Is Best

🧪 Testing a Class with Constructor Injection
@Test
void testNotificationService() {
EmailService mockEmail = Mockito.mock(EmailService.class);
NotificationService service = new NotificationService(mockEmail);
service.notifyUser("ravi@example.com");
verify(mockEmail).sendEmail("ravi@example.com");
}
Clean, isolated unit testing made easy.
❓ What If I Have Multiple Beans of the Same Type?
Use @Qualifier
to specify which bean to inject:
@Bean
@Qualifier("sms")
public MessageService smsService() {
return new SmsService();
}
@Bean
@Qualifier("email")
public MessageService emailService() {
return new EmailService();
}
And in your constructor:
@Autowired
public NotificationService(@Qualifier("email") MessageService service) {
this.messageService = service;
}
Spring @Qualifier Annotation Example - In this tutorial, we will see how to use Spring or Spring Boot @Qualifier…www.javaguides.net
🔄 Summary

✅ Final Thoughts
Dependency Injection is one of the most powerful features of Spring — and one of the reasons why it’s so widely adopted.
Instead of writing glue code to wire up objects, you focus on your business logic, and let Spring do the heavy lifting.
With DI, you write less code, test better, and build cleaner applications.
Comments
Post a Comment
Leave Comment