티스토리 뷰
제어의 역전(IoC, Inversion of Control)
일반 자바 프로그램은 객체를 직접 생성하여 메소드를 호출한다. 만약 A object 에서 B object 의 메소드를 사용하고 싶으면, A object 에서 B object 를 직접 생성한 다음 메소드를 호출한다.
하지만 Spring 은 구현한 객체가 아니라 Spring 이 객체의 생명주기를 관리한다. 개발자가 Service 객체의 동작을 구현하더라도 해당 객체가 언제 호출되는지, 메소드를 언제 실행하는지는 Spring 이 관리한다. 프로그램의 제어권이 역전된 것이다.
예를 들어 JUnit 프레임워크에는 유닛 테스트를 도와주는 annotation 이 존재한다. @Test 나 @BeforeEach , @AfterEach 같은 annotation 을 붙이면 JUnit 프레임워크가 해당 메소드의 호출 시점을 알아서 정한다.
따라서 IoC 개념을 도입하면 다음과 같은 장점을 얻을 수 있다.
- 프로그램의 진행 흐름과 구체적인 구현을 분리시킬 수 있다.
- 개발자는 비즈니스 로직에 집중할 수 있다.
- 구현체 사이의 변경이 용이하다.
- 객체 간 의존성이 낮아진다.
의존성 주입(DI, Dependency Injection)
앞서 든 예시처럼 'A object 에서 B object 를 직접 생성한 다음 B object 의 메소드를 호출한다.' 와 같은 상황이라면 A 가 B 에 의존한다 라고 말할 수 있다. 이와 달리 외부에서 B 를 생성한 다음 A 에게 넘기는 행위를 의존성 주입이라고 한다. 여기서 B 를 인터페이스로 만들면 A 에게 다양한 구현체를 주입할 수 있다.
의존성 주입 방법
1. Field Injection
변수 선언부에 @Autowired annotation 을 사용한다.
@Controller
public class SampleController {
@Autowired
private SampleService sampleService;
}
Field Injection 은 여러모로 위험한 방법이다.
우선 annotation 을 붙이는데 제한이 없다. 극단적인 예시로 한 클래스에 변수가 1,000개가 있고 모든 변수에 @Autowired annotation 을 붙이면 그만큼 다른 클래스와 의존성이 높아진다.
순환 참조가 일어날 수도 있다. A class 가 B class 를 참조, B class 가 C class 를 참조, C class 가 A class 를 참조.
다시 말해 A → B → C → A 가 일어날 수 있다는 뜻이다. 이렇게 되면 서로가 서로를 호출하다가 결국 StackOverFlowError 가 발생하고 만다.
2. Setter Injection
setter 에 @Autowired annotation 을 선언한다.
@Controller
public class SampleController {
private SampleService sampleService;
@Autowired
public void setSampleService(SampleService sampleService) {
this.sampleService = sampleService;
}
}
상황에 따라 의존성을 선택적으로 주입할 수 있다.
Setter Injection 도 위험한 상황이 발생할 수 있다. 대표적으로 주입이 필요한 객체가 주입되지 않았음에도 생성될 수 있다. 예를 들어 Service 구현체를 주입해야 하는 Controller 를 Service 주입 없이 생성한 다음 Service 내 메소드를 호출하면 NullPointerException 이 발생한다.
3. Constructor Injection
constructor 에 @Autowired annotation 을 선언한다.
@Service
public class SampleService {
private SampleDAO sampleDAO;
@Autowired
public SampleService(SampleDAO sampleDAO) {
this.sampleDAO = sampleDAO;
}
}
@Controller
public class SampleController {
private final SampleService sampleService = new SampleService(new SampleDAO());
}
Spring 에서는 Constructor Injection 을 권장한다.
Constructor Injection 은 Field Injection 에서 발생할 수 있는 순환 참조와 Setter Injection 에서 발생할 수 있는 NullPointerException 을 방지한다.
주입받을 필드를 final 로 선언하여 불변성을 유지할 수도 있다.
'Spring' 카테고리의 다른 글
Spring 관점 지향 프로그래밍(AOP) 개념 (0) | 2023.02.05 |
---|---|
스프링(Spring), 스프링 부트(Spring Boot) 개념 (0) | 2023.01.30 |