LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 817 min read

Dependency Injection Basics

Learn what Dependency Injection is and see a simple, complete example of one class being injected into another by Spring.

Introduction

You have seen Dependency Injection (DI) sprinkled throughout earlier lessons — now let's slow down and focus on it directly. This lesson defines DI precisely, walks through a complete working example, and introduces the different ways Spring can perform injection, setting up Lesson 9's deeper comparison of constructor and setter injection.

What is Dependency Injection?

Definition

Dependency Injection is a pattern where an object receives ("is injected with") the other objects it depends on from an external source, rather than creating those objects itself.

A "dependency" is simply any object that another object needs in order to do its job. If class A uses class B, then B is a dependency of A. Without DI, A would create its own instance of B. With DI, something else — in our case, the Spring container — creates B and hands it to A.

A Simple DI Example

Let's build a small example: a NotificationService that depends on an EmailSender to actually deliver messages.

EmailSender.java
package com.programinds.demo;
import org.springframework.stereotype.Component;
@Component
public class EmailSender {
public void send(String message) {
System.out.println("Sending email: " + message);
}
}
NotificationService.java
package com.programinds.demo;
import org.springframework.stereotype.Service;
@Service
public class NotificationService {
private final EmailSender emailSender;
// Spring injects an EmailSender instance here automatically
public NotificationService(EmailSender emailSender) {
this.emailSender = emailSender;
}
public void notifyUser(String userMessage) {
emailSender.send(userMessage);
}
}
AppConfig.java
package com.programinds.demo;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.programinds.demo")
public class AppConfig {
}
MainApp.java
package com.programinds.demo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
NotificationService service = context.getBean(NotificationService.class);
service.notifyUser("Your order has shipped!");
}
}
Console Output

Click Run to see what this code prints.

How Spring Resolves the Dependency

When Spring builds NotificationService, it inspects its constructor and sees it requires an EmailSender. It looks in its registry of bean definitions for a bean of type EmailSender, finds the one created from our @Component class, creates it (if not already created), and passes it into the NotificationService constructor. All of this happens automatically — we never called "new EmailSender()" anywhere.

Types of Dependency Injection

TypeHow It WorksWhen Used
Constructor InjectionDependencies are passed as constructor argumentsRecommended default for required dependencies
Setter InjectionDependencies are set via a public setter methodUseful for optional dependencies
Field InjectionDependencies are injected directly into fields via @AutowiredConvenient but discouraged for testability reasons

Field Injection (and Why to Avoid It)

You will frequently see field injection in older tutorials because it looks compact.

Field injection (not recommended)
@Service
public class NotificationService {
@Autowired
private EmailSender emailSender;
public void notifyUser(String userMessage) {
emailSender.send(userMessage);
}
}
Why This Is Discouraged

Field injection hides dependencies (you cannot tell what a class needs without reading every field), makes the class impossible to instantiate without Spring or reflection in tests, and allows the object to exist in a half-initialized state before injection completes. Lesson 9 covers constructor injection, the preferred alternative, in depth.

Common Mistakes

Avoid These Mistakes
  • Defaulting to field injection with @Autowired out of habit instead of using constructor injection.
  • Forgetting that the dependency class must also be a Spring bean (annotated with @Component or similar) for injection to work.
  • Creating circular dependencies, where two beans each require the other in their constructors, which Spring cannot resolve by default.

Best Practices

  • Prefer constructor injection for all required dependencies — it makes dependencies explicit and enables easy unit testing.
  • Mark injected fields as "final" when using constructor injection so the compiler enforces they are always set.
  • Keep the number of dependencies a single class needs small; too many constructor parameters is a sign the class is doing too much.

Frequently Asked Questions

No, not if the class has only one constructor — Spring 4.3+ automatically uses it for injection without needing @Autowired.

It throws a NoSuchBeanDefinitionException at startup, telling you exactly which dependency it could not satisfy.

Yes, a constructor can accept as many dependencies as needed, and Spring will inject each one from the container.

Key Takeaways

  • Dependency Injection means a class receives its dependencies from an external source instead of creating them.
  • Spring inspects constructors (or setters/fields) to determine what a bean needs and supplies matching beans automatically.
  • Constructor injection is generally preferred; field injection is common but discouraged.
  • Every dependency involved must itself be a Spring-managed bean.

Summary

Dependency Injection is what makes Inversion of Control practical: Spring automatically supplies each bean with the collaborators it declares, using the constructor in our NotificationService example. Next, we will compare constructor and setter injection directly and see exactly why constructor injection has become the default recommendation.

Next Lesson →

Constructor vs Setter Injection