Backend Development 8 min read

Understanding Dependency Injection and IoC in Spring: From Direct Instantiation to Factory Pattern and Reflection

This article explains why dependency injection reduces coupling compared with direct object creation, walks through traditional instantiation, interface programming, factory methods, and reflection, and shows how Spring IoC combines these techniques to manage object lifecycles and improve code maintainability.

Top Architect
Top Architect
Top Architect
Understanding Dependency Injection and IoC in Spring: From Direct Instantiation to Factory Pattern and Reflection

In many Java learning scenarios, beginners wonder why using dependency injection (DI) can lower coupling compared with directly creating objects with new . This article walks through the reasoning step by step.

1. Traditional object creation

Creating objects directly ties the client code to concrete classes:

WuliCar wuli = new WuliCar();
wuli.run();

BaomaCar baoma = new BaomaCar();
baoma.run();

ZhiShenJi zhi = new ZhiShenJi();
zhi.fly();

If the business logic needs to replace a car with a helicopter, every occurrence of the concrete class must be changed, which is error‑prone and labor‑intensive.

2. Interface programming

Defining a common interface decouples the client from concrete implementations:

public interface Vehicle {
    // Define a vehicle with a work method
    void work();
}

public class Baoma implements Vehicle {
    @Override
    public void work() {
        System.out.println("宝马跑起来");
    }
}

public class ZhiShenJi implements Vehicle {
    @Override
    public void work() {
        System.out.println("直升机飞起来");
    }
}

Now the client only depends on the Vehicle interface, but the creation code is still scattered.

3. Factory method

A factory class centralizes object creation:

class VehicleFactory {
    VehicleFactory() {}
    public static Vehicle getInstance(String type) {
        Vehicle result = null;
        if ("car".equals(type)) {
            result = new Car();
        }
        if ("zhishenji".equals(type)) {
            result = new ZhiShenJi();
        }
        return result;
    }
}

Vehicle vehicle = VehicleFactory.getInstance("car");
vehicle.work();

Adding a new vehicle type still requires modifying the factory, and the client must still specify the concrete type string.

4. Reflection

Using reflection (or configuration files) removes the hard‑coded type from the client code:

Vehicle vehicle = VehicleFactory.getInstance(readConfigFile());
vehicle.work();
// Desired type: zixingche
zixingche.work();

Now the only place to change is the configuration, dramatically reducing coupling.

5. Spring IoC

Spring's Inversion of Control container combines the factory pattern and reflection to automatically create and wire objects, managing their lifecycle and dependencies without any explicit new statements in business code.

6. Summary

Dependency injection means the required objects are provided by a container rather than created manually, achieving loose coupling.

The caller only declares the component it needs; the container handles lookup, creation, and lifecycle.

Frameworks like Spring abstract object creation, allowing developers to focus on business logic.

JavareflectionIoCSpringdependency injectionFactory Pattern
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.