삽질 주도 개발
article thumbnail
Published 2022. 4. 29. 02:01
@Primary, @Qualifier Spring

오늘은 같은 타입의 빈이 여러 개인 경우 식별하는 방법을 포스팅한다.

 


 

주제는 빈을 식별하는 방법으로 코드의 내용은 신경쓰지 않고 단순히 구분을 위해 작성했다.

public interface MailService {

    void sendEmail(String email);
    
}
@Service
public class NaverMailServiceImpl implements MailService {

    @Override
    public void sendEmail(String email) {
        System.out.println("Send naver mail to " + email);
    }

}
@Service
public class GoogleMailServiceImpl implements MailService {

    @Override
    public void sendEmail(String email) {
        System.out.println("Send google mail to" + email);
    }

}

 

@Service
public class UserService {

    private final MailService mailService;

    public UserService(MailService mailService) {
        this.mailService = mailService;
    }

    public String createUser(SignUpDto signUpDto) {
        // ... create user
        return mailService.sendEmail(signUpDto.getEmail());
    }
}

 

우리가 어떠한 문제를 해결하기 위해 다형성을 이용하여 의존성을 주입받는 경우가 있다. 위의 경우 mailService 인터페이스를 특정 구현체를 상속받아 서비스를 이용하게 될 것인데, 여러 타입의 빈이 존재하는 상황에서 스프링은 의존성을 어떻게 식별할 수 있을까?

 

 

field name

 

첫 번째는 바로 이름으로 식별하는 방법이다.

 

스프링은 의존성 주입 시 타입을 찾으면서 두 개 이상의 같은 빈을 가지는 경우, 필드 이름(Autowired) 그리고 파라미터의 이름(constructor)을 통해 한 번 더 찾는다.

@Service
public class UserService {

    private final MailService mailService;

    public UserService(MailService naverMailServiceImpl) {
        this.mailService = naverMailServiceImpl; // 파라미터를 실제 구현체 클래스 이름과 같도록 작성
    }

    public String createUser(SignUpDto signUpDto) {
        // ... create user
        return mailService.sendEmail(signUpDto.getEmail());
    }
}
@Service
public class UserService {

    @Autowired
    private MailService naverMailServiceImpl;

    public String createUser(SignUpDto signUpDto) {
        // ... create user
        return naverMailServiceImpl.sendEmail(signUpDto.getEmail());
    }
    
}

 

테스트

@SpringBootTest
class UserServiceTest {

    @Autowired
    UserService subject;

    @Test
    void 필드명으로_빈을_식별한다() {
        SignUpDto userDto = SignUpDto.builder()
                .name("woogie")
                .email("rxjw95@gmail.com")
                .build();

        String response = subject.createUser(userDto);

        assertEquals(response, "Send naver mail to rxjw95@gmail.com");

    }

}

 

 

 

@Primary

 

두 번째는 @Primary 어노테이션을 사용하면 된다. 이름과 같이 해당 어노테이션이 붙으면 가장 먼저 빈으로 주입하게 된다.

@Primary
@Service
public class NaverMailServiceImpl implements MailService {

    @Override
    public String sendEmail(String email) {
        return "Send naver mail to " + email;
    }

}
@Service
public class UserService {

    private final MailService mailService;

    public UserService(MailService mailService) {
        this.mailService = mailService;
    }

    public String createUser(SignUpDto signUpDto) {
        // ... create user
        return mailService.sendEmail(signUpDto.getEmail());
    }
}

 

테스트

@SpringBootTest
class UserServiceTest {

    @Autowired
    UserService subject;

    @Test
    void Primary_어노테이션으로_빈을_식별한다() {
        SignUpDto userDto = SignUpDto.builder()
                .name("woogie")
                .email("rxjw95@gmail.com")
                .build();

        String response = subject.createUser(userDto);

        assertEquals(response, "Send naver mail to rxjw95@gmail.com");

    }

}

 

@Qualifier

마지막은 @Qualifier 어노테이션을 사용하는 방법이다. 해당 어노테이션도 이름을 보고 알 수 있듯이 식별자를 두어 빈을 식별할 수 있다.

 

파라미터로 받을 빈과 해당 빈을 선언한 클래스에 모두 작성해주어야 한다.

 

@Qualifier("naver")
@Service
public class NaverMailServiceImpl implements MailService {

    @Override
    public String sendEmail(String email) {
        return "Send naver mail to " + email;
    }

}
@Service
public class UserService {

    private final MailService mailService;

    public UserService(@Qualifier("naver") MailService mailService) {
        this.mailService = mailService;
    }

    public String createUser(SignUpDto signUpDto) {
        // ... create user
        return mailService.sendEmail(signUpDto.getEmail());
    }
}

 

테스트

@SpringBootTest
class UserServiceTest {

    @Autowired
    UserService subject;

    @Test
    void Qualifier_어노테이션으로_빈을_식별한다() {
        SignUpDto userDto = SignUpDto.builder()
                .name("woogie")
                .email("rxjw95@gmail.com")
                .build();

        String response = subject.createUser(userDto);

        assertEquals(response, "Send naver mail to rxjw95@gmail.com");

    }

}
@Qualifier("naver")
@Service
public class NaverMailServiceImpl implements MailService {

    @Override
    public String sendEmail(String email) {
        return "Send naver mail to " + email;
    }

}