Spring의 의존성 주입(DI) 4가지 방식 [ @Autowired, 생성자, 컬렉션, Setter ]
DI(Dependency Injection, 의존성 주입)란?
👉 객체를 직접 생성하는 대신, 외부에서 주입해주는 방식
👉 코드의 유지보수성 증가, 객체 간 결합도 감소
🔥 1️⃣ 자동(Annotation 기반) DI (@Autowired)
💡 Spring이 자동으로 객체를 주입해주는 방식
✅ @Autowired 어노테이션을 사용하면, Spring이 해당 타입의 Bean을 자동으로 찾아서 주입해줌
✅ XML 설정 없이 간단하게 사용할 수 있음
📌 코드 예제
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component // Engine 클래스를 Bean으로 등록
public class Engine {
private String type = "V8 Engine";
public String getType() {
return type;
}
}
@Component // Car 클래스를 Bean으로 등록
public class Car {
private Engine engine;
@Autowired // 자동으로 Engine 객체 주입
public void setEngine(Engine engine) {
this.engine = engine;
}
public void showEngineType() {
System.out.println("Car engine: " + engine.getType());
}
}
📌 실행 코드
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext("com.example");
Car car = context.getBean(Car.class);
car.showEngineType(); // 출력: Car engine: V8 Engine
}
}
✅ @Autowired 방식의 장점
✔ XML 설정 없이 편리하게 사용 가능
✔ 타입을 기준으로 주입되므로 코드가 간결해짐
🔥 2️⃣ 생성자(Constructor) DI
💡 객체 생성 시, 생성자를 통해 필요한 의존성을 주입하는 방식
✅ 필수적인 의존성 주입에 적합함 (불변성 유지 가능)
✅ Spring 4.3 이상에서는 @Autowired 생략 가능
📌 코드 예제 (XML 방식)
<bean id="engine" class="com.example.Engine">
<constructor-arg value="V8 Engine"/>
</bean>
<bean id="car" class="com.example.Car">
<constructor-arg ref="engine"/>
</bean>
📌 Java 코드
public class Engine {
private String type;
public Engine(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
public class Car {
private Engine engine;
public Car(Engine engine) { // 생성자를 통해 Engine 객체를 받음
this.engine = engine;
}
public void showEngineType() {
System.out.println("Car engine: " + engine.getType());
}
}
✅ 생성자 DI 방식의 장점
✔ 객체가 생성될 때 반드시 필요한 값이 설정됨 (필수 의존성)
✔ 객체가 불변(Immutable)하게 유지될 수 있음
🔥 3️⃣ 컬렉션(Collection) DI
💡 여러 개의 객체(List, Set, Map)를 한꺼번에 주입하는 방식
✅ 여러 개의 Bean을 한 번에 주입할 때 사용
✅ List, Set, Map 같은 컬렉션 타입으로 묶어서 주입 가능
📌 코드 예제 (XML 방식)
<bean id="engine1" class="com.example.Engine">
<constructor-arg value="V8 Engine"/>
</bean>
<bean id="engine2" class="com.example.Engine">
<constructor-arg value="Electric Engine"/>
</bean>
<bean id="garage" class="com.example.Garage">
<property name="engines">
<list>
<ref bean="engine1"/>
<ref bean="engine2"/>
</list>
</property>
</bean>
📌 Java 코드
import java.util.List;
public class Garage {
private List<Engine> engines;
public void setEngines(List<Engine> engines) { // 컬렉션 주입
this.engines = engines;
}
public void showEngines() {
for (Engine engine : engines) {
System.out.println("Engine type: " + engine.getType());
}
}
}
📌 실행 코드
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Garage garage = context.getBean("garage", Garage.class);
garage.showEngines();
// 출력:
// Engine type: V8 Engine
// Engine type: Electric Engine
✅ 컬렉션 DI 방식의 장점
✔ 여러 개의 객체를 한꺼번에 관리 가능
✔ 리스트, 맵 등 다양한 자료구조를 쉽게 주입 가능
🔥 4️⃣ Setter DI
💡 Setter 메서드를 이용해서 의존성을 주입하는 방식
✅ 선택적으로 의존성을 설정할 때 유용
✅ 객체를 먼저 만들고, 이후에 값을 설정할 수 있음
✅ 객체를 만든 후, set메서드를 이용해 값을 넣어주는 방식이야!
✅ 객체가 만들어진 후에도 값을 변경할 수 있어 유연함
2️⃣ Java 코드 작성
✅ Engine 클래스 (엔진)
package com.example;
public class Engine {
private String type; // 엔진 종류
public void setType(String type) { // Setter 메서드 (나중에 값을 설정)
this.type = type;
}
public String getType() { // 엔진 종류 가져오기
return type;
}
}
✅ Engine 객체를 만들 때 바로 값을 넣지 않고, 나중에 setType() 메서드로 설정 가능!
✅ Car 클래스 (자동차)
package com.example;
public class Car {
private Engine engine; // 자동차가 사용할 엔진
public void setEngine(Engine engine) { // Setter 메서드 (나중에 엔진을 설정)
this.engine = engine;
}
public void showEngineType() {
if (engine != null) {
System.out.println("Car engine: " + engine.getType());
} else {
System.out.println("Engine not installed!");
}
}
}
✅ Car 객체를 만들 때 바로 엔진을 설정하지 않아도 됨!
✅ 나중에 setEngine() 메서드를 호출해서 엔진을 설정할 수 있음.
3️⃣ XML 설정 (beans.xml)
💡 Spring이 자동으로 객체를 만들고, Setter 메서드로 값을 넣어줌!
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Engine 객체 생성 -->
<bean id="engine" class="com.example.Engine">
<property name="type" value="V8 Engine"/> <!-- setType("V8 Engine") 실행됨! -->
</bean>
<!-- Car 객체 생성 -->
<bean id="car" class="com.example.Car">
<property name="engine" ref="engine"/> <!-- setEngine(engine) 실행됨! -->
</bean>
</beans>
🔍 XML 코드 설명
1️⃣ Spring이 engine 객체를 먼저 생성
2️⃣ setType("V8 Engine") 메서드를 자동 실행해서 엔진 종류를 설정
3️⃣ 그 다음 car 객체를 생성
4️⃣ setEngine(engine)을 자동 실행해서 car에 engine을 넣어줌
✅ Spring이 알아서 setType()과 setEngine()을 실행해줌!
4️⃣ 실행 코드 (Main.java)
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
// 🔥 Spring 컨테이너 실행 (XML 설정을 읽어옴)
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 🚗 "car"라는 id의 Bean(객체) 가져오기
Car car = context.getBean("car", Car.class);
// 🚗 자동차의 엔진 정보 출력
car.showEngineType(); // 출력: Car engine: V8 Engine
}
}
5️⃣ 실행 결과
Car engine: V8 Engine
🎯 자동차(Car)가 먼저 만들어지고, Spring이 setEngine()을 실행해서 엔진(Engine)을 넣어줬다!
🎯 즉, 객체를 먼저 만들고, 나중에 값을 넣는 방식이 Setter 주입!
📌 4가지 DI 방식 비교 정리
DI 방식주입 방식특징장점단점
자동(@Autowired) | @Autowired 사용 | Spring이 자동으로 주입 | XML 없이 간결 | 같은 타입의 Bean이 여러 개면 오류 발생 가능 |
생성자(Constructor) DI | 생성자를 통해 주입 | 불변성 유지 가능, 필수 의존성 주입 | 코드 안정성 높음 | 생성자에 의존성이 많아지면 복잡해질 수 있음 |
컬렉션(Collection) DI | List, Set, Map을 주입 | 여러 객체를 한 번에 주입 가능 | 그룹으로 객체 관리 가능 | 복잡한 설정 필요 |
Setter DI | Setter 메서드로 주입 | 선택적 의존성 설정 가능 | 유연한 설정 가능 | 필수 의존성을 강제할 수 없음 |
🔥 결론
- @Autowired(자동 주입) → Spring이 자동으로 관리, 가장 간편함
- 생성자 주입 → 필수 의존성 주입에 적합, 불변성을 보장
- 컬렉션 주입 → 리스트, 맵 등의 여러 객체를 한 번에 주입 가능
- Setter 주입 → 선택적 의존성 주입 가능, 유연한 설정 가능