
의존성 주입이란 객체가 가지고 있는 다른 객체에 대한 의존성을 외부에서
주입해 주는 것이다.
일반적으로 객체가 스스로 다른 객체의 생성자를 호출하여 의존성을 결정하는것의 반대의 흐름이기에 제어의 역전 (Inversion of Control) 이라고 한다.
Spring에서는 Spring 컨테이너가 이러한 역할을 해준다.
컴포넌트의 로직과 의존성 간에 구분이 잘 된다
의존성이 주입된 컴포넌트는 독립적이기에 다른 프로젝트에서도
쉽게 재사용 할 수 있다
의존성 주입은 개별 컴포넌트간의 단위테스트를 간소화 시킨다
코드가 더 깔끔해지며 Decoupling이 더 효과적이다.
그렇기에 개별의 코드 변경이 다른부분에 영향을 미치지 않는다
현재 회원의 메신저와 관련된 서비스를 만들고 있다고 가정하자.
메신저는 카카오톡, WhatsApp, 텔레그램 등 선택지가 많다.
만약에 카카오톡과 관련된 메신저 서비스를 처리하기 위해서는 다음과 같이
코드를 작성해야 할 것이다.
public class MemberController {
private KakaotalkService kakaotalkService = new KakaotalkService();
public void sendMessage() {
kakaotalkService.sendMessage();
}
}
인터페이스를 사용하여 조금더 추상화에 의존하도록 만들어 보자
public class MemberController {
private MessengerService messengerService = new KakaotalkService();
public void sendMessage() {
messengerService.sendMessage();
}
}
모든 메신저 서비스들은 MessengerService라는 인터페이스를 상속하게 하여
인터페이스에 의존할 수 있도록 하였다.
하지만 만약에 이때 텔레그램메신저를 사용하게 되었다고 하면
public class MemberController {
private MessengerService messengerService = new TelegramService();
public void sendMessage() {
messengerService.sendMessage();
}
}
이렇게 객체 생성 부분 코드를 변경해야 한다.
이는 객체지향 설계원칙인 SOLID 설계원칙중 O에 해당하는
Open and Closed Principle에 부합하지 못한다.
확장에는 열려있지만 수정에는 닫혀있어야 한다.
그렇다면 어떻게 해야할까?
외부에서 객체를 누군가 주입해준다면 어떻게 될까?
public class MemberController {
private MessengerService messengerService;
public void MemberController(MessengerService messengerService) {
this.messengerService = messengerService;
}
public void sendMessage() {
messengerService.sendMessage();
}
}
public class Container {
public void inject() {
MessengerService messengerService = new TelegramService();
MemberController controller = new MemberController(messengerService);
}
}
이렇게 MemberController는 더 이상 어떤 구체적인 메신저를 사용할지 모르고
외부에서 메신저를 결정해주게 된다면 사용할 메신저가 변경된다고
MemberController에서 코드의 수정이 발생할 필요가 없어지는 것이다.
이렇게 객체를 생성하고 주입해주는 역할을 Spring 컨테이너가 해주는 것이다.
생성자를 통해서 의존 관계를 주입받는 형식이다.
생성자 호출시 딱 1번만 호출되는것이 보장된다.
생성자의 매개변수로 의존성을 주입하기에 final 키워드가 사용가능하다.
그렇기에 의존성을 가지고 있는 객체가 바뀌지 않게 불변 보장이 가능하다.
코드
@Controller
public class MemberController {
private final MessengerService messengerService;
@Autowired
public MemberController (MessengerService messnegerService) {
this.messengerService = messnegerService;
}
}
생성자가 하나 뿐일때는 @Autowired가 생략 가능하며
Lombok을 활용하면 더 깔끔하게 코드를 작성할 수 있다
@RequiredArgsConstructor
@Controller
public class MemberController {
private final MessengerService messengerService;
}
@RequiredArgsConstructor 가 생성자를 대신 만들어 주기 때문에
코드가 훨씬 간결해졌다.
말 그대로 Setter를 통해 의존성을 주입하는 경우이다.
Bean의 생성과 의존성 주입이 별개로 일어난다.
코드
@Controller
public class MemberController {
private MessengerService messengerService;
@Autowired
public void setMessengerService(MessengerService messnegerService) {
this.messengerService = messnegerService;
}
}
의존성을 필드에 바로 주입하는 방법이다.
테스트할때 불편하며 순환참조가 발생할 수 있는 등 다양한 문제를 가지고 있다.
코드
@Controller
public class MemberController {
@Autowired
private MessengerService messengerService;
}
참조
[Dependency Injection]
(https://docs.spring.io/spring-framework/reference/core/beans/dependencies/factory-collaborators.html)
(https://phoenixnap.com/kb/dependency-injection)
[Decoupling]
(https://dev.to/ivangavlik/decoupling-using-example-in-java-4cgb)