01 Spring Dependency Injection
01 Spring Dependency Injection
1. DI
1. 의존성
- 어떤 클래스가 다른 클래스(객체)의 기능을 사용해야 할 때 “의존성이 있다.”
- Car가 Tire 객체를 사용해야 굴러갈 수 있다면, Car는 Tire에 의존
1 2 3
public class Car { private Tire tire = new KoreaTire(); // Car는 KoreaTire에 의존 }
2. 의존성 주입(Dependency Injection)
- 의존성을 외부(스프링 컨테이너 등)에서 넣어주는 방식
- 객체가 스스로 필요한 의존성을 만들지 않고, 외부에서 “주입”받음.
1 2 3 4 5 6 7 8 9 10 11 12
public class Car { private Tire tire; // 생성자 주입 받음 public Car(Tire tire) { this.tire = tire; } public void drive() { System.out.println("Driving with " + tire.getBrand()); } }
1 2 3 4 5
<!-- XMl방식으로 스프링컨테이너에서 주입해줌. --> <bean id="koreaTire" class="com.example.KoreaTire"/> <bean id="car" class="com.example.Car"> <constructor-arg ref="koreaTire"/> </bean>
- 기존에는 new를 통해서 개발자가 직접 무엇을 할지 결정해서했으나, 외부에서 주입을 하게됨.
- 개발자는 인터페이스 또는 흐름을 가지고 어떤 객체가 들어와야한다까지 파악함.
3. note
- 프레임워크에서 대신 주입한다고 해서, 개발자가 무엇을 주입할지 전혀 모르는 것은 아님.
- 전반적인 로직의 흐름을 파악해야하고, 사용될 빈들을 등록해야하고, 빈들이 매칭될수 있게 네이밍 규칙등을 지켜야함.
- 단순히 연결 자체를 프레임워크에서 진행함.
- 따라서 클래스가 바뀌거나 로직이 변경되는 등의 상황이 발생했을 때,
- 개발자가 작성하는 클래스 내부에 특정 클래스를 지칭하는 것이 없기 때문에,
- 프레임워크의 규칙에 따라서 빈의 이름과 매칭만 잘해주면 문제가 없게됨.
2. Bean의 등록
1. Bean
- 스프링 컨테이너(IoC 컨테이너)가 생성하고 관리하는 객체를 스프링에서는 “ Bean “
- 일반 자바 객체지만 스프링이 관리하면 그 순간부터 “Bean”
2. Bean의 특징
- 생명주기 관리: 객체 생성 → 의존성 주입 → 초기화 → 소멸까지 컨테이너가 관리
- 의존성 관리: 다른 Bean과 연결(주입)하는 역할 수행
- 싱글톤 기본 전략: 기본적으로 컨테이너 내에서 하나의 Bean 인스턴스만 공유 (필요 시 prototype 등 다른 스코프 가능)
3. Bean의 등록
1. xml
1
<bean id="car" class="com.example.Car"/>
2. java Config
1
2
3
4
5
6
7
8
@Configuration
public class AppConfig {
@Bean
public Car car() {
return new Car();
}
}
// 빈이름은 car가 됨 => 메서드기준
3.어노테이션
- 빈 생성
1 2 3 4 5 6 7
@Component public class Car { } // 일반적으로 아무 이름을 지정하지 않으면 클래스명에서 첫 글자를 소문자로 바꾼 것이 빈 이름 @Component("myCar") public class Car { } //이경우에는 myCar가됨.
- 종류
어노테이션 | 용도 | 빈 이름 기본 규칙 | 설명 |
---|---|---|---|
@Component | 일반 빈 | 클래스명 → 첫 글자 소문자 | 가장 일반적인 컴포넌트 등록용 어노테이션 |
@Service | 서비스 계층 빈 | 클래스명 → 첫 글자 소문자 | 비즈니스 로직 구현 클래스에 주로 사용, 기능적 의미 부여 |
@Repository | DAO / 데이터 접근 빈 | 클래스명 → 첫 글자 소문자 | DB 관련 예외 변환 처리 자동 적용 |
@Controller | 웹 컨트롤러 빈 | 클래스명 → 첫 글자 소문자 | MVC 패턴에서 요청 처리 담당 |
@RestController | REST API 컨트롤러 빈 | 클래스명 → 첫 글자 소문자 | @Controller + @ResponseBody 합친 것 |
@Configuration | 설정 클래스 빈 | 클래스명 → 첫 글자 소문자 | @Bean 정의를 포함할 수 있는 설정용 클래스 |
@Bean | 자바 Config에서 빈 등록 | 메서드명 | @Configuration 클래스 내에서 사용, 메서드 반환 객체를 빈으로 등록 |
4. note
- ApplicationContext
1 2 3 4 5
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Car car = context.getBean("car", Car.class); car.drive();
- ApplicationContext는 스프링 IoC 컨테이너의 구현체 중 하나로, 빈 생성과 관리, 의존성 주입을 담당
- 필요한 경우가 생기면 직접 끄집어내서 사용하기도 함.
- 자바빈 스프링빈
구분 | 자바빈 | 스프링빈 |
---|---|---|
정의 | 자바 규약에 따른 객체 | 스프링 컨테이너가 관리하는 객체 |
관리 주체 | 개발자가 직접 new | 스프링 IoC 컨테이너 |
규칙 | 기본 생성자, getter/setter | 특별한 규약 없음 (단, 컨테이너 등록 필요) |
목적 | 데이터 캡슐화, 컴포넌트화 | 의존성 관리, 객체 생명주기 관리 |
3. 빈의 주입
1. 생성자 주입(Constructor Injection)
- 생성자를 통해 의존성을 주입
- 불변성 보장 (final 필드 가능)
- 필수 의존성을 강제할 수 있음
1 2 3 4 5 6 7 8 9
@Component public class Car { private final Tire tire; @Autowired public Car(Tire tire) { this.tire = tire; } }
2. 세터 주입(Setter Injection)
- 세터 메서드를 통해 의존성을 주입
- 선택적 의존성 주입에 유리
- 필수 의존성을 강제할 수 없음
1 2 3 4 5 6 7 8 9
@Component public class Car { private Tire tire; @Autowired public void setTire(Tire tire) { this.tire = tire; } }
3. 필드 주입(Field Injection)
- 필드에 직접 @Autowired 붙여서 주입
- 코드 간결
- 테스트 어렵고, 순환 의존성 문제 발생 가능 → 권장되지 않음
1 2 3 4 5
@Component public class Car { @Autowired private Tire tire; }
4. 주입을 바꾸는 케이스
1. 주입을 바꾸는 케이스
- 주입을 바꿔야 하는 상황 → 거의 선택적 의존성, 테스트, 런타임 환경 변화, 전략 교체 정도
- 대부분은 생성자 주입으로 불변성을 보장하는 것이 안전
- 바꿔야 할 필요가 있는 경우만 세터 주입 활용
2. note
- 컨테이너가 아닌 개발자가 관리하는 형태가됨.
구분 | 직접 주입 | 자동 주입 |
---|---|---|
객체 생성 | 개발자가 new | 스프링 컨테이너 |
의존성 연결 | 개발자가 setter/생성자 호출 | 스프링이 @Autowired, @Bean 등으로 연결 |
장점 | 자유롭게 런타임 변경 가능 | 코드 간결, 유지보수 용이 |
단점 | 관리 번거로움 | 런타임에 임의 변경 어려움 |
3. 케이스
1. 테스트용 Mock 교체
1
2
3
4
5
6
@Test
public void testDrive() {
Car car = new Car();
car.setTire(new MockTire()); // 세터 주입으로 테스트용 객체 교체
car.drive();
}
2. 특정 기능이 활성화될 때만 의존성을 주입하고 싶을 때
1
2
3
4
5
if(userWantsPremiumFeature) {
car.setNavigation(new AdvancedNavigation());
} else {
car.setNavigation(new BasicNavigation());
}
3. 다국어, 지역별 설정, 또는 환경 변수에 따라 런타임 시 다른 Bean 선택
1
2
3
4
5
if(region.equals("US")) {
car.setTire(new AmericanTire());
} else {
car.setTire(new KoreaTire());
}
This post is licensed under CC BY 4.0 by the author.