Step4.DI/IoC에서 DI에 대해 이야기 해본적이 있다.
그리고 크게 3가지 주입방식이 있고, 그중에서 생성자 주입방식이 권장된다고 말했었는데
오늘은 3가지 주입방식의 특징과 생성자 주입방식이 권장되는 이유에 대해서 알아보자.
public class MenuController{
private final MenuService menuService;
@Autowired
public MenuController(MenuService menuService){ //생성자
this.menuService = menuService;
}
}
public class MenuController{
@Autowired
private MenuService menuService;
}
public class MenuController{
private final MenuService menuService;
@Autowired
public setMenuService(MenuService menuService){ //setter
this.menuService = menuService;
}
}
public class A{
@Autowired
private final B b;
public void acall(){
b.bcall();
}
}
public class B{
@Autowired
private final A a;
public void bcall(){
a.acall();
}
}
객체 생성과 의존관계 주입시점이 분리되어있기 때문이다.
따라서 필드주입, setter주입방식은 객체(bean)생성 이후, 의존관계 주입이 수행되고 해당 비즈니스 로직에서 순환 참조가 일어나기 때문에 사전(어플리케이션 구동 시점)에 감지하고 경고할 수 없다.
반면 생성자 주입방식의 경우 생성과 동시에 의존관계 주입이 수행되므로 어플리케이션 구동 시점에 순환참조를 발견할 수 있다.
생성자 주입은 주입받은 의존성을 클래스 내부에서 변경할 수 없도록 한다.
따라서 클래스의 불변성과 안전성을 보장할 수 있다.(final선언이 가능하다.)
반대로 Setter주입의 경우 setter의 외부 호출로인해 객체가 변경될 가능성이 있다.
의존 관계의 변경이 필요한 경우는 매우 적고, 이 매우 적은 가능성때문에 불필요한 수정의 가능성을 열어두는 것은 SOLID의 OCP(개방폐쇄원칙)을 위반하는것이 된다.
ocp(개방폐쇄원칙) : 모듈은 확장에는 열려있어야 하고, 변경에는 닫혀있어야 한다.
스프링 DI컨테이너에 독립적인 테스트코드를 작성할 수 있다.
main코드가 필드 주입방식으로 작성된 경우 순수한 자바 코드로 unit test하는것은 불가능하다.
main코드는 Spring 프레임워크 위에서 동작하지만 테스트코드는 Junit으로 동작한다.
따라서 필드 주입 방식은 DI컨테이너의 도움 없이는 테스트코드를 작성할 수 없다.