class A{
B b = new B();
}
class B{
public B(){}
}
🤔 위의 코드를 한 번 보자.
현재 A라는 클래스 안에서 B라는 객체를 사용하고 있다. 그리고 사용하기 위해 new라는 키워드를 통해 객체를 직접 생성해서 사용하고 있다. 이런 경우 A라는 클래스가 B에 의존하고 있는 것이다. 이 경우에 B의 다른 구현체로 바꾸고 싶다면 A 코드를 직접 수정해야 한다.
현재 A가 B에 너무 강하게 의존하고 있기 때문에 유지보수성, 확장, 테스트의 어려움이라는 문제가 발생한다. 이 문제의 해답이 DI이다.
Dependency Injection(의존성 주입)은 객체 간의 의존 관계를 IoC Container가 빈의 설정 정보를 바탕으로 자동적으로 연결해주는 것을 의미한다.
1-1에서 작성한 코드에 의존성 주입을 한다면⁉️
@Component
class A {
private final B b;
@Autowired // 의존성 주입 by.생성자
public A(B b) {
this.b = b;
}
}
@Component
class B {
public B() {}
}
의존성을 주입하는 방식을 예제로 볼 것인데, 의존성을 주입하는 과정 그 자체에 대해서만 보여줄 것이다. 그래서 주입 방식을 보기 전에 주입되는 객체가와 사용되는 부분에 관해 먼저 짚고 넘어갈 것이다.
BookDAO : 인터페이스BookDAOImpl : BookDAO를 구현하고 있는 클래스로 @Repository 어노테이션을 통해 bean으로 등록되어 있다.BookDTO : 도서 정보가 있는 DTO@Service
public class BookService {
private BookDAO bookDAO;
public BookService(){}
@Autowired
public BookService(BookDAO bookDAO) {
this.bookDAO = bookDAO;
}
}
✅ Spring 4.3 버전 이후부터는 생성자가 1개 뿐이라면 어노테이션을 생략해도 자동으로 생성자 주입이 동작한다.
@Service
public class BookService {
private BookDAO bookDAO;
@Autowired(required = false)
public void setBookDAO(BookDAO bookDAO){
this.bookDAO = bookDAO;
}
}
required 속성을 false로 설정하면 의존 대상의 bean이 존재하지 않는 경우에 의존성을 형성하지 않는다.@Service
public class BookService {
@Autowired
private BookDAO bookDAO;
우리는 의존성을 주입하는 3가지 방법을 배웠다. 그런데 Spring에서는 이 3가지 방법 중에서 "생성자 주입"을 권장한다.
Spring: “We recommend constructor injection for mandatory dependencies and setter injection for optional dependencies.”
✅ Why : 왜 생성자 주입이 권장될까❓
객체가 생성 될 때 모든 의존성이 주입 되기 때문에 의존성을 보장할 수 있다.
객체의 불변성을 보장할 수 있다.
final 키워드를 사용할 수 있다.해당 객체가 어떤 의존성을 가지고 있는지 명확하게 알 수 있기 때문에 코드 가독성이 좋다.
DI 컨테이너와 결합도가 낮기 때문에 테스트 하기 좋다. 테스트를 할 때 스프링 컨테이너 없이 테스트를 할 수 있다.
Lombok의 @RequiredArgsConstructor 어노테이션과 결합되어 유용하게 사용할 수 있다.