DI(Dependency Injection)란 애플리케이션에서 객체 간의 의존 관계를 외부에서 주입하는 것을 말한다.
의존 관계란 특정 대상에 의존하여 관계를 맺고 있는 것으로, 의존 대상이 변경되면 의존하고 있는 대상이 영향이 가는 관계이다.
만약 A 클래스에서 B 클래스를 참조하여 사용하고 있다면, A클래스는 B클래스에 의존하고 있다고 한다.
DI를 사용하면 객체 지향에서 강조하는역할과 책임을 분리하여,유연하고 변경에 용이한 애플리케이션을 만들 수 있다.
아래 예시와 같이 Car 클래스와 Engine 인터페이스, Engine 인터페이스의 구현 클래스 두개가 있다고 가정해보자
Car 클래스는 Engine 인터페이스의 구현 클래스인 GasolineEngine 인스턴스를 클래스 내부에 직접 생성하여 사용하고 있다
그런데 만약 Engine을 DieselEngine 로 변경하려 한다면 Car 클래스의 engine 객체 생성부분을 직접 수정해야 한다.
public class Car {
private Engine engine = new GasolineEngine();
public void start() {
engine.start();
System.out.println("Car started");
}
}
public interface Engine {
void start();
}
public class GasolineEngine implements Engine {
public void start() {
System.out.println("Gasoline engine started");
}
}
public class DieselEngine implements Engine {
public void start() {
System.out.println("Diesel engine started");
}
}
이는 의존관계 대상이 수정됨에 따라 클라이언트의 코드도 수정해야 한다는 점에 있어서
객체 지향 원칙인 단일 책임 원칙(SRP), 개방 폐쇄 원칙(OCP), 의존관계 역전 원칙(DIP)를 위반하고 있다.
이러한 문제를 해결하기 위해서는 아래와 같이 Car 클래스에서 Engine 인터페이스 구현체를 생성 하는 것이 아닌, 외부에서 구현 클래스를 정하도록 변경해야 한다.
이렇게 외부에서 객체 간 의존 관계를 설정하여 의존 대상이 변경되어도 클라이언트 코드에는 영향이 가지 않도록 해주는 기술이 DI (Dependency Injection) 이다.
public class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
System.out.println("Car started");
}
}
public interface Engine {
void start();
}
public class GasolineEngine implements Engine {
public void start() {
System.out.println("Gasoline engine started");
}
}
public class DieselEngine implements Engine {
public void start() {
System.out.println("Diesel engine started");
}
}
DI(Dependency Injection) 주입 방법은 아래와 같이 생성자 주입, 세터 주입, 필드 주입 3가지가 있다.
객체를 생성할 때 생성자를 통해 의존성을 주입하는 방법으로, 생성자를 통해 의존하는 객체를 외부에서 전달받아 필드에 할당한다.
public class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
// ...
}
Spring 프레임워크에서는 아래와 같은 이유로 DI 주입 시 생성자 주입 방법을 권장하고 있다.
Setter 메서드를 통해 의존성을 주입하는 방법으로, 외부에서 의존하는 객체를 설정하는 Setter 메서드를 제공해서 객체를 필드에 할당한다.
public class Car {
private Engine engine;
// Setter 메서드
public void setEngine(Engine engine) {
this.engine = engine;
}
// ...
}
주입할 의존 객체를 필드로 선언하고, @Autowired 어노테이션을 사용하여 Spring Container로 부터 주입받는 방법이다.
필드 주입 시에는 주입 받으려는 대상이 Spring Bean으로 등록되어 있어야 한다.
public class Car {
@Autowired
private Engine engine;
// ...
}