IoC(Inversion of Control)
- 제어의 역전(IoC)이란 객체의 생성 및 생명주기 관리에 대한 책임이 개발자에서 프레임워크에게 넘어가는 것
- Spring을 사용했을 때의 강력한 이점 중 하나가 Bean을 Spring Container에 등록을 해두고, 필요한 경우 등록된 Bean을 주입하여 자동으로 필요한 객체를 받을 수 있음
- 개발자가 직접 객체 생성, 관리를 하지 않아도 되기 때문에 제어의 역전이라고 함
- 기존 방식
public class A {
private B b;
pulbic A(){
this.b = new B();// 직접 객체를 생성하여 넣어줌
}
}
- IoC
public class A{
private B b;
public B(B b){
this.b = b; // 객체를 생성하지 않음
}
}
DI(Dependency Injection)
- 객체를 직접 생성하지 않고 외부(스프링 컨테이너)에서 받아 사용하는 방식
- 의존성이란 하나의 객체(A)가 다른 객체(B)를 필요할 때, A는 B에 의존하고 있다고 표현
- 의존성 주입이란 하나의 객체(A)가 필요로 하는 다른 객체(B)를 직접 생성하지 않고, 외부에서 주입해주는 방식
public class A{
@Autowired
private B b;
}
의존성 주입의 방식 3가지
1. 필드 주입
- 클래스의 멤버 변수 위에 @Autowired를 작성
public class TestController{
@Autowired
private TestService testService;
}
2. Setter 주입
- Setter 메서드 위에 @Autowired를 작성하여 주입
public class TestController{
private TestService testService;
@Autowired
public void setTestService(TestService testService){
this.testService = testService;
}
}
3. 생성자 주입
- 생성자 위에 @Autowired를 작성하여 주입
public class TestController{
private TestService testService;
@Autowired
public TestController(TestService testService){
this.testService = testService;
}
}
Spring 4.3버전 이후부터는 필드 주입과 Setter 주입을 지양하고 생성자 주입 지향
- 이유 ⇒ 순환 참조
- 순환 참조란 A가 B를 참조하고, B가 A를 참조하는 것을 의미하는 데 이런 경우 서버가 죽어버림
- 필드주입, Setter 주입
- 실행 시 Bean을 생성한 후, 주입하려는 빈을 찾아 주입
- 객체 생성 시점에 빈을 준비하기 때문에 서로 참조하는 객체가 생성되지 않은 상태에서 해당 빈을 참조하기 때문에 오류가 발생
- 생성자 주입
- 생성자의 인자에 사용되는 빈을 찾거나 빈 팩토리에서 생성
- 무조건 빈을 생성하는 것이 아니라 주입하려는 빈이 있다면 그것을 사용, 없을 경우 빈을 생성하기 때문에 순환 참조가 일어나지 않는다고 함
- 필드주입, Setter 주입
순환 참조 추가 설명
@Service
public class AService {
@Autowired
private BService bService;
}
@Service
public class BService {
@Autowired
private AService aService;
}
순환 참조 발생 이유
- 빈 생성 과정
- Spring은 애플리케이션 컨텍스트를 초기화할 때 @Service 애노테이션이 붙은 클래스들을 빈으로 생성
@Service 이외에도 @Component, @Controller, @Repository도 빈 생성
- Spring은 애플리케이션 컨텍스트를 초기화할 때 @Service 애노테이션이 붙은 클래스들을 빈으로 생성
- AService 빈 생성
- AService 빈을 생성하려고 시도(시도일 뿐 생성이 아님)
- AService 빈은 BService를 필요로 하므로 BService 빈의 인스턴스를 주입하려고 함
- BService의 빈이 생성되는 것을 대기하는 것으로 이해
- BService 빈 생성
- BService를 주입하기 위해 Spring이 BService 빈을 생성하려고 시도(시도일 뿐)
- 그러나 BService는 AService를 필요로 하기 때문에 AService 빈의 인스턴스를 주입하려고 함
- 순환 참조 발생
- AService는 BService의 빈이 필요, BService는 AService의 빈이 필요
- 즉, 서로가 서로를 필요로 하여 무한정 대기하는 상태로 이해가 되며, 이것은 마치 서로의 자원을 다 쓸 때까지 기다리는 DeadLock 상태와 비슷한 케이스라고 이해가 됨
- AService는 BService의 빈이 필요, BService는 AService의 빈이 필요
- 따라서, 생성자 주입을 지향하며 생성자 주입 시 다음의 장점을 가짐
- 순환 참조 방지
- 테스트 코드 작성 용이
- 원하는 객체를 생성한 후 생성자에 넣음
- 코드 악취 제거
- 객체 변이 방지
- final을 선언함으로써 주입된 객체의 불변성을 보장
- 생성자 주입의 단점
- 주입해야할 것이 늘어날 때마다 생성자에 계속 추가해야 하기 때문에 매우 번거로움
public class Test{
private final A a;
private final B b;
private final C c;
private final D d;
@Autowired
public Test(A a, B b, C c, D d){
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
}
- 위의 단점으로 인하여 @RequiredArgsConstructor라는 애노테이션이 등장
- 반드시 final로 선언해야만 주입이 됨
@RequiredArgsConstructor
public class Test{
private final A a;
private final B b;
private final C c;
private final D d;
}
결론
- 의존성 주입(DI)는 필드 주입 / Setter 주입 / 생성자 주입 세 가지로 나뉘지만 순환 참조 같은 심각한 에러와 각종 편의성 등의 장점을 가진 생성자 주입을 사용하는 것을 권장
- 생성자 주입의 주입해야할 객체가 N개일 경우 생성자의 매개변수 개수, 대입하는 코드가 N개만큼 추가가 되기 때문에 @RequiredArgsConstructor 등장
- 주의 사항은 멤버 필드 앞에 final을 사용하지 않으면 의존성 주입이 되지 않기 때문에 유의할 것
- @RequiredArgsConstructor를 사용하고 final을 붙이지 않을 경우 다음과 같이 NullPointException 발생
'Language > Spring' 카테고리의 다른 글
[Spring Boot] 이메일 인증 코드를 이용한 회원 가입 (0) | 2024.08.13 |
---|---|
[Spring Boot] 구글 이메일 인증 코드 발급 구현 (0) | 2024.08.13 |
JpaAuditing 사용 방법 및 예제 (0) | 2024.08.12 |
BeanFactory, ApplicationContext (0) | 2024.08.10 |
Spring Framework와 Spring Boot Framework (0) | 2024.08.10 |