Dependency Injection (concepts & Spring basics)

Dependency injection is the technique of giving an object the collaborators it needs from the outside — through its constructor — instead of having it create them itself, which decouples your code and makes it far easier to test.

Learn Dependency Injection (concepts & Spring basics) in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice…

Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

You should be comfortable with interfaces (DI depends on programming to abstractions) and constructors . A look at lambdas helps for the functional-interface examples.

💡 Analogy: A class that new s its own dependencies is like a lamp with its power cable soldered straight into the wall — it works, but you can never move it, test it on a bench, or swap the bulb's power source. Dependency injection gives the lamp a plug and the wall a socket : the lamp just declares "I need power" and whoever installs it decides what to plug in — mains power, a battery pack, or a test rig. The lamp doesn't change; only what you plug in does.

"Inversion of Control" is simply this idea: the lamp no longer controls where its power comes from — an outside installer does.

When a class builds its own collaborators with new , it is welded to that exact implementation. You can't swap EmailService for an SMS one, and you certainly can't test the class without triggering the real thing. That is tight coupling .

Depend on an interface and receive the implementation through the constructor. Now the same OrderService works with an email notifier, an SMS notifier, or anything else — and the caller (a manual "injector") decides which to plug in.

Because the dependency is an interface passed in, a test can inject a fake that records calls instead of doing real work. You can then assert exactly what the class did — with no email server, no network, no flakiness.

In a small program you do the wiring in main . In a large app that quickly becomes a tangle, so frameworks like Spring provide a container that wires everything for you. You annotate classes as beans with @Component / @Service , and Spring constructs the object graph and injects dependencies — often just by spotting the constructor (so @Autowired is optional on a single constructor).

Answer: control over creating and wiring collaborators. Instead of the class deciding what to new , an outside party (your code or a container) decides and supplies it.

Answer: so any implementation — real or fake — can be injected. Depending on the concrete class re-couples you to it and blocks substitution.

Answer: because it's set once in the constructor and never reassigned. final documents that, prevents accidental rebinding, and guarantees the object is fully initialised.

🎯 YOUR TURN — Inject a Logger

Make Calculator depend on the Logger interface via constructor injection, store it final , and log each addition.

🧩 MINI-CHALLENGE — Two injected collaborators

Build a CheckoutService that takes a PaymentGateway and a Receipts recorder, wired with lambdas in main .

You now understand Inversion of Control, why hard-wired new dependencies hurt, how to apply constructor injection against an interface in plain Java, how that unlocks easy testing with fakes, and how Spring's @Component / @Autowired automate the same idea at scale.

Next up: Serialization — turning objects into bytes (and back), with its serialVersionUID, transient fields, and security caveats.

Practice quiz

What is dependency injection?

  • Creating dependencies inside a class with new
  • Injecting code at runtime via reflection only
  • Supplying a class's dependencies from the outside
  • A type of inheritance

Answer: Supplying a class's dependencies from the outside. DI means a class receives the objects it needs (its dependencies) from outside rather than constructing them itself.

What is Inversion of Control (IoC)?

  • Control of object creation/wiring moves out of the class to a caller or container
  • The class controls everything
  • Reversing a list
  • Running code backwards

Answer: Control of object creation/wiring moves out of the class to a caller or container. IoC flips who is in charge: instead of a class creating its collaborators, an external party provides them. DI is one way to achieve IoC.

Which is the preferred form of injection?

  • Field injection
  • Static injection
  • No injection
  • Constructor injection

Answer: Constructor injection. Constructor injection makes dependencies explicit and lets you mark them final, ensuring the object is fully and validly built.

Why does DI improve testability?

  • It makes code faster
  • You can inject fakes/mocks instead of real collaborators
  • It removes all bugs
  • It hides dependencies

Answer: You can inject fakes/mocks instead of real collaborators. Because a class depends on an abstraction passed in, tests can supply a fake implementation and assert on interactions — no real I/O.

DI works best when classes depend on...

  • interfaces / abstractions
  • concrete classes
  • static methods
  • global variables

Answer: interfaces / abstractions. Depending on an interface lets any implementation (real or fake) be injected, which is the core of decoupling.

In plain Java, who plays the role of the 'injector'?

  • The JVM
  • The garbage collector
  • The code that constructs and wires the objects (e.g. main or a factory)
  • Nobody

Answer: The code that constructs and wires the objects (e.g. main or a factory). Without a framework, your own wiring code (in main or a factory) creates implementations and passes them into constructors.

What does Spring's @Component annotation do?

  • Deletes a class
  • Marks a class as a bean for Spring to manage and inject
  • Makes a class final
  • Starts a thread

Answer: Marks a class as a bean for Spring to manage and inject. @Component (and @Service, @Repository) registers the class in Spring's container so it can be created and injected automatically.

What does @Autowired tell Spring to do?

  • Run the method twice
  • Log the call
  • Cache the result
  • Inject a matching bean into the field/constructor

Answer: Inject a matching bean into the field/constructor. @Autowired asks Spring to supply a managed bean of the required type; on a constructor it's often optional in modern Spring.

A benefit of decoupling via DI is...

  • Faster compilation only
  • You can swap implementations without changing the dependent class
  • Smaller JARs
  • No need for interfaces

Answer: You can swap implementations without changing the dependent class. Because the class only knows the interface, you can swap Email for SMS, or real for fake, without touching it.

Constructor injection lets you mark dependencies as...

  • static
  • volatile
  • final
  • transient

Answer: final. Dependencies assigned once in the constructor can be final, signalling they never change after construction.