본문 바로가기
Spring

스프링 @Autowired : 같은 타입의 빈이 여러개라면?(@Qualifier, @Primary)

by jeonghaemin 2021. 2. 26.
728x90

스프링에서 자동 의존성 주입을 사용할 때, 같은 타입의 빈이 여러 개 조회되는 상황에 도움이 되는 몇 가지에 대해 알아보자.

목차

  • @Autowired
  • @Qualifier
  • @Primary
  • 같은 타입의 빈을 모두 조회하고싶다면?

@Autowired

의존성 자동 주입에 사용되는 애노테이션인 @Autowired는 먼저 애노테이션이 적용된 필드, 파라미터의 타입을 기준으로 빈을 조회한다.

같은 타입의 빈이 여러 개 조회되면 추가적으로 필드 또는 파라미터의 이름으로 빈을 조회한다.

다음과 같이 HelloService 인터페이스와 두 개의 구현체가 있다.

public interface HelloService {

   String hello();

}

@Component
public class HelloServiceEng implements HelloService{

    @Override
    public String hello() {
        return "Hello";
    }

}

@Component
public class HelloServiceKor implements HelloService {

    @Override
    public String hello() {
        return "안녕";
    }

}

HelloService 타입으로 자동 의존성 주입을 시도하면 구현체가 2개이기 때문에 예외가 발생한다.

(예제 코드에서는 필드 주입을 사용하였다. 생성자 주입, 수정자 주입에서도 결과는 동일)

@Autowired HelloService helloService;

필드 이름을 helloServiceKor로 바꾸면 HelloServiceKor 구현체가 주입된다.

//HelloServiceKor이 주입된다.
@Autowired HelloService helloServiceKor;

@Qualifier

@Qualifier 애노테이션을 사용하여 추가적인 구분자를 사용할 수 있다. 사용 방법은 빈을 등록할 때, 빈을 주입할 때 @Qualifier 애노테이션에 구분에 사용할 문자열을 전달하여 사용하면 된다.

@Component
@Qualifier("mainHelloService") 
public class HelloServiceKor implements HelloService {

    @Override
    public String hello() {
        return "안녕";
    }

}

//또는 빈을 직접 등록할때도 사용 가능.

@Bean
@Qualifier("mainHelloService") 

빈을 주입 시에도 동일하게 같은 이름을 전달하여 해당 빈이 주입되도록 한다.

//필드 주입 예시
@Autowired
@Qualifier("mainHelloService")
HelloService helloService;

//생성자 주입 예시
public HelloController(
@Qualifier("mainHelloService") HelloService helloService) {
    this.helloService = helloService;
}

의존성을 주입할 때 @Qualifier("mainHelloService")를 선언한 빈이 없다면? mainHelloService라는 이름의 빈을 추가로 찾는다. 그래도 찾을 수 없다면 NoSuchBeanDefinitionException이 발생한다.

@Primary

@Primary의 사용법은 정말 간단하다. 우선권을 갖고자하는 빈에 애노테이션을 붙여주면 된다.

@Component
@Primary
public class HelloServiceEng implements HelloService{

    @Override
    public String hello() {
        return "Hello";
    }
}

HelloService 타입의 빈 주입 시 HelloServiceEng이 주입된다.

@Autowired
HelloService helloService;

@Test
@DisplayName("HelloServiceEng가 주입된다.")
void helloServiceTest() {
        Assertions.assertSame(HelloServiceEng.class, helloService.getClass());
 }

//테스트 통과

참고로 스프링에서 자동보다는 수동, 넓은 범위보다는 좁은 범위의 설정이 더 높은 우선순위를 가진다. 따라서 @Primary와 @Qualifier 둘 중에 @Qualifier가 우선순위가 높다.

같은 타입의 빈을 모두 조회하고 싶다면?

  • 의존성 주입 시 List 또는 Map을 사용해서 같은 타입의 빈을 한 번에 모두 조회할 수 있다.
@Autowired
Map<String, HelloService> helloServiceMap;

@Autowired
List<HelloService> helloServices;

@Test
@DisplayName("같은 타입의 빈 모두 조회하기")
void helloServiceTest() {
    //Map으로 조회
    for (String name : helloServiceMap.keySet()) {
            System.out.println("name = " + name + ", bean = " + helloServiceMap.get(name));
     }

     //List로 조회
     for (HelloService helloService : helloServices) {
             System.out.println("helloService = " + helloService);
     }
}

/* 실행 결과
name = helloServiceEng, bean = com.example.springstudy.HelloServiceEng@1867b831
name = helloServiceKor, bean = com.example.springstudy.HelloServiceKor@21f8acfd
helloService = com.example.springstudy.HelloServiceEng@1867b831
helloService = com.example.springstudy.HelloServiceKor@21f8acfd
*/

참고

댓글