생성자 주입으로 대부분의 사용, 필요할때 가끔 수정자 주입 방식을 사용. 여기서는 스프링을 사용하는 경우의 예제를 대부분 볼 것이다.
1. MessageService 인터페이스 정의
public interface MessageService {}
2.MessageService 인터페이스를 이용한 EmailService, SMSService 구현
public class EmailService implements MessageService {}
public class SMSService implements MessageService {}
3."MessageService" 인터페이스를 사용하는 "MessageClient"라는 클래스를 만듭니다
public class MessageClient {
private MessageService messageService;
public MessageClient(MessageService messageService) {
this.messageService = messageService;
}
}
4. 생성자 주입.
public class Main {
public static void main(String[] args) {
MessageClient emailClient = new MessageClient(new EmailService());
MessageClient smsClient = new MessageClient(new SMSService());
}
}
"MessageClient" 클래스는 "MessageService" 인터페이스에 의존하며, 해당 생성자를 통해 의존성이 제공됩니다. "MessageClient"가 인스턴스화되면 생성자 주입을 사용하여 "EmailService" 또는 "SMSService" 구현을 제공합니다.
순수한 자바 코드로 작성시, Main에서 생성자를 사용할때, new EmailService() 나 new SMSService() 등의 구현체를 알아야한다. 이는 객체지향 설계 방법을 위반한다. 구현체에 의존하면 안되고 추상화에 의존해야 하는데, 우리는 EmailService나 SMSService라는 구현체를 확실하게 알아야 사용.
위의 예제에서 사용한 MessageService 인터페이스를 그대로 활용.
1. 구현체인 EmailService와 SMSService에 Spring의 애노테이션 @Service를 붙여준다.
이렇게 되면 자동으로 Spring이 구현체를 인식해서 DI를 할 수 있다.
@Service
public class EmailService implements MessageService {}
2. MessageClient도 @Component 애노테이션을 붙여서 Scan의 대상임을 알려준다.
@Autowired는 생성자가 하나라면 생략이 가능하다.
@Component
public class MessageClient {
private final MessageService messageService;
@Autowired
public MessageClient(MessageService messageService) {
this.messageService = messageService;
}
}
Spring Framework를 사용시, 구현체(Email, SMSService)를 직접 입력할 필요가 없이 Spring이 자동으로 관리해서 의존성주입을 해준다. 이를 통해 우리는 MessageService라는 추상적인 객체만 알면 되고, 구현체는 알 필요가 없다. 또한 이를 통해 구현체만 만들어주면 Spring이 자동으로 의존성을 관리해주기 때문에 쉽게 확장을 할 수 있다.
@Component
public class MessageClient {
private MessageService messageService;
@Autowired
public void setMessageService(MessageService messageService) {
this.messageService = messageService;
}
@Component
public class MessageClient {
@Autowired
private MessageService messageService;
}
-> 한번에 여러 필드에 주입 받을 수 있다. 사용하지 않는다.
@Autowired(required = false) => 자동 주입할 대상이 없으면 수정자 매서드 자체가 호출이 안됨.
@Nullabe => 자동 주입할 대상이 없으면 Null이 입력된다.
Optional<> -> 자동 주입할 대상이 없으면 Optional.empty가 입력된다.
@Nullable , Optional =>>스프링전반에걸쳐지원된다. 예를 들어서 생성자 자동 주입에서 특정 필드에만 사용해도 됨.
@Autowired -> 타입으로 조회한다.
*조회 빈이 2개 이상 일시 -문제
NoUniqueBeanEfinitionException -오류
문제 해결 방법
@Autowired 필드명 ->여러 빈이 있으면 필드이름, 파라미터 이름으로 빈 이름을 추가 매칭한다.
@Qualifier => 따로 @Qualifier에 이름을 붙여 @Qualifier를 찾는 용도로 사용해서 매칭시켜 준다.
@Primary -> 자주 사용하는 방법. 우선 순위를 지정하는 방법이다. @Primary가 붙어있다면 우선순위로 시작된다.
@Qualifier("emailService") 이렇게 문자를 적으면 컴파일시 타입 체크가 안된다.(실수로 잘못된 문자를 입력가능) -> 애노테이션을 만들어서 문제를 해결 할 수 있다.
@emailService ->라는 애노테이션을 만들어서 @Qualifier를 조합해서 사용하면 된다. => 또한 이렇게 만든다면 이 애노테이션을 이용해 사용된 곳을 바로 다 조회 할 수도있다.
조회한 빈이 모두 필요할때 - List, Map
전략 패턴을 쉽게 구성할 수 있다 -> ex)메일을 Email과 SMS를 선택해서 적용가능.
참고 : 본 글은 김영한님의 스프링 강의를 정리한 것이다.