@Autowired 애너테이션을 이용한 의존성 주입은 타입을 기반으로 동작을 한다.
(의존성 주입이 필요한 경우 스프링 컨테이너는 타입을 기반으로 Bean을 찾는다.)
만약 찾지 못할 경우, NoSuchBeanDefinitionException 에러 발생
@Service
@RequiredArgsConstructor
public class NotificationService{
private final NotificationChannel notificationChannel;
}
public interface NotificationChannel {
void notify(Account account, String message);
}
@Component
public class EmailNotificationChannel implements NotificationChannel {
@Override
public void notify(Account account, String message){...}
}
NotificationService 컴포넌트의 멤버 변수 notificationChannel에는 EmailNotification Channel 빈이 들어간다.
(타입 기반으로 Bean을 찾기 때문이다.)
어떤 빈을 주입할지 몰라서, NoUniqueBeanDefinitionException 에러를 발생한다.
따라서 @Qualifier나 @Primary 어노테이션을 사용한다.
@Service
@RequiredArgsConstructor
public class NotificationService{
private final List<NotificationChannel> notificationChannels;
}
타입과 일치하는 모든 스프링 빈을 찾아 List의 요소로 넣어주는 방식으로 처리를 한다.
@Service
@RequiredArgsConstructor
public class NotificationService{
private final EmailNotificationChannel emailNotificationChannel;
...
public void notify(Account account, String message){
switch(account.getNotificationType()){
case EMAIL:
...
case SLACK:
...
case CHAT:
...
...
// 이런식의 Switch - Case는 OCP를 위반한다.
// 새로운 필드가 추가될 때마다 case가 늘어난다.
}
}
}
위 코드를 switch가 아닌 for문으로 하자!
@Service
public class NotificationService{
private final List<NotificationChannel> notificationChannels;
public void notify(Account account, String message){
for(NotificationChannel notificationChannel : notificationChannels){
...
}
}
}
public interface Notification{
...
}
EmailNotificationChannel, SlackNotificationChannel, ChatlNotificationChannel 모두 Notification을 구현한다.
(그러면 알아서 List NotificaitonChannel에 들어간다.)
어떤 객체가 메소드를 처리하는 와중에 자신이 갖고 있는 다른 메소드를 호출하는 상황을 의미한다.
class Something {
public void doSomething1(){
doSomething2();
}
public void doSomething2(){...}
}
스프링의 빈 메소드에서 발생하는 자가 호출은 개발자의 의도와 다르게 작동할 수 있다.
특히 AOP 어노테이션에서 문제가 된다.
@Service
@RequiredArgsConstructor
class MyService{
public void doSomething1(){
doSomething2();
}
@Transactional
public void doSomething2(){
// do Something...
}
}
자가 호출이 발생하면 호출된 메소드에 적용된 AOP 어노테이션이 동작하지 않을 수 있으므로
트랜잭션이 제대로 동작하지 않을 수 있다.
왜냐하면 AOP는 프록시를 기반으로 동작하기 때문이다.
MyController -> MyServiceProxy -> MyService 구조임.
따라서, 자가 호출로 트랜잭션을 실행시킬 수 없다.