기록/Spring framework

의존 자동주입 할때 타입이 일치하는 빈 중 의존 주입할 대상 빈을 선택하는 방법: @Qualifer 이용법. 2021-11-12

최동훈1 2021. 11. 12. 15:28

그런데 의문점이 생길 것이다. 만약 @Autowired를 사용해서 의존 자동주입의 대상이 되는 빈을 컨테이너에서 찾을때, 컨테이너에 등록되어 있는 해당 타입과 일치하는 빈이 여러개이면 어떤 일이 일어날까?

우선 테스트를 해보자.

@Configuration
@Import(AppCtx2.class) 
public class AppCtx1 {
	
	@Bean
	public MemberDao memberdao() {
		MemberDao m=new MemberDao();
		return m;
	}
	@Bean
	public MemberDao memberdao2() {
		MemberDao m=new MemberDao();
		return m;
	}
	@Bean
	public MemberRegisterService mrsvc() {
		MemberRegisterService mrsvc=new MemberRegisterService();
		return mrsvc;
	}
	@Bean
	public PasswordChangeService pcsvc() {
		PasswordChangeService pcsvc=new PasswordChangeService(memberdao());
		return pcsvc;
	}
}

이렇게 스프링 설정 클래스에  @Autowired로 주입해줄 MemberDao 타입의 빈을 하나 추가 해 줬다.

여기서 내가 또 이해되지 않은 것이 있는데, 그럼 분명 싱글톤이라 했는데 같은 빈 객체가 2개 생기는 것인가..? 라는 의문점이 생겼지만, 스프링 컨테이너에 등록되는 빈 식별자는 설정 메서드의 "이름" 인 memberdao() 와 memberdao2() 이기 때문에 같은 빈 객체가 아닌 각각의 다른 빈 객체라는 것을 이해하였다.

 

실행을 해 보면, 

No qualifying bean of type 'chap04.MemberDao' available: expected single matching bean but found 2: memberdao,memberdao2

이런 오류가 나온다. 즉, 1개의 타입 빈이 일치해야 하는데, 의존주입을 할려고, 스프링 컨테이너에 @Autowired가 붙은 빈의 타입을 찾아보니, 2개가 나왔다는 뜻이다.

 

이런 문제를 해결하기 위해서는, 스프링 컨테이너에 입력할 빈의 "식별자"를 자동주입 전용으로 "재정의 할 필요가 있다." 즉, 일반적인 스프링 빈으로써의 식별자는 메서드의 이름인 "memberdao() 와 memberdao2()" 이지만, @Autowired 애너테이션을 통한 자동주입 한정으로, 또 다른 식별자를 붙이는 것이다.

 

이떄, 사용되는 애너테이션이 @Qualifier 애너테이션이다. 이 애너테이션을 사용해서 앞서 정의한 같은 타입의 MemberDao 객체를 한정해 보겠다.

@Configuration
@Import(AppCtx2.class) 
public class AppCtx1 {
	
	@Bean
	@Qualifier("dao1")
    // 같은 타입의 빈 객체를 구별하기위한 한정자를 지정함.
	public MemberDao memberdao() {
		MemberDao m=new MemberDao();
		return m;
	}
	@Bean
	@Qualifier
    //이렇게 한정자로 별도의 이름을 지정하지 않았을 경우에는, 
    빈 객체의 식별자가 한정자로 됨.
	public MemberDao memberdao2() {
		MemberDao m=new MemberDao();
		return m;
	}
	@Bean
	public MemberRegisterService mrsvc() {
		MemberRegisterService mrsvc=new MemberRegisterService();
		return mrsvc;
	}
	@Bean
	public PasswordChangeService pcsvc() {
		PasswordChangeService pcsvc=new PasswordChangeService(memberdao());
		return pcsvc;
	}
}

 

또 @Qualifier를 이용해서는 한정자를 명시적으로 지정하는 경우와 그렇지 않은 경우가 있는데 위의 내가 작성한 사례처럼, 별도로 지정하지 않고, @Qualifier만 붙인다면 "빈 객체의 식별자인 메서드 이름"이 그대로 "한정자"가 된다.

 

public class MemberRegisterService {
	@Autowired 
	@Qualifier("dao1")
    //자동 의존 주입할 빈의 한정자를 지정하였다.
	private MemberDao memberdao;

	public MemberRegisterService(MemberDao memberdao) {
		this.memberdao = memberdao;
	}
	public MemberRegisterService() {
		// TODO Auto-generated constructor stub
	}
	
	public void registermember(Member m) {
		Member m1=memberdao.getidentifier(m);
		if(m1!=null) {
			throw new DuplicateMemberException("등록된 멤버의 ID가 중복되어있습니다.");
		}
		else {
	memberdao.register(m);
	System.out.println("등록완료 ID: "+m.getId()+" pwd: "+m.getPwd() );
		}
		
	}

}

 

이렇게 실제 클래스에 의존 자동주입을 위한 애너테이션 밑에 한정자를 작성하니, 정상적으로 작동이 되었다.

 

또 주의해야 할 점은 만약 상속관계에 있는 조상타입의 빈을 자동주입 할 때도, 만약에 스프링 컨테이너에 자동주입할 빈을 상속한 자손타입의 빈이 있다면 동일한 익셉션이 난다. 이유는 다형성 때문이다. 이건 당연한 문제이기에 별도의 예시는 안들겠다.