개발바닥곰발바닥
728x90

해당 포스팅은 인프런 김영한님의 스프링 핵심원리 - 기본편 강의 내용을 바탕으로 작성하였습니다.

@Qualifier, @Primary 어노테이션

Spring Boot에서 어노테이션을 통해 자동으로 빈을 컨테이너에 설정하는 경우, 같은 인터페이스의 구현체 클래스 두 개 이상이 빈으로 등록되면

NoUniqueBeanDefinitionException: No qualifying bean of type 'test.a' available: expected single matching bean but found 2

위와 같이 1 개의 빈만 매칭되야 하지만 2개의 빈이 존재한다며 오류가 발생한다.

이럴 경우 Spring Boot가 어떤 빈을 주입해야 하는지 알려줘야 한다.

Autowired된 field의 이름을 빈 이름으로 설정하는 방법, @Qulifier 어노테이션을 사용하는 방법, @Primary 어노테이션을 사용하는 방법 세 가지가 있는데 각각 설명해 보겠다.

빈과 구현체 정보

동물 인터페이스와 그걸 구현한 개와 고양이를 구현 클래스로 만들어 빈으로 등록하고 AnimalService 클래스 생성자에 주입되도록 예시 코드를 만들었다.

현재 상태로는 위에서 기술한 오류가 발생하는데, 설명한 세 가지 방법을 예시로 코드가 어떻게 수정되는지 확인해보도록 한다.

public interface Animal {
	String sound();
}

@Component
public class Dog implements Animal {
	public String sound() {
		return "왈왈";
	}
}
@Component
public class Cat implements Animal {
	public String sound() {
		return "야옹";
	}
}

@Service
public class AnimalService {
	private final Animal animal;
	@Autowired
	public AnimalService(Animal animal) {
			this.animal = animal;
	}
}

Autowired 변수명을 빈 이름으로 설정

Autowired로 생성자에 주입되는 Animal 객체의 변수명을 구현체 클래스의 이름인 dog으로 만들면, Animal 클래스의 빈이 두 개인 것을 확인한 Spring Boot가 dog 이름과 같은 빈이 있는지 찾아서 주입해준다.

같은 타입의 빈이 여러 개 있으면 필드 이름이나 파라미터 이름으로 다시 매칭하는 특성을 이용한 것이다. 변수명이 아니라 생성자의 파라미터명을 dog으로 변경해도 아래 코드와 같은 동작을 수행한다.

@Service
public class AnimalService {
	private final Animal dog;
	@Autowired
	public AnimalService(Animal animal) {
			this.animal = animal;
	}
}

@Qualifier 어노테이션 사용

Qualifier 어노테이션은 빈에 추가 구분자를 붙여주는 방법으로 생성자에서 해당 구분자를 명시하면 그 구분자를 가진 빈을 주입해준다. 헷갈리기 쉬운 점은 Qualifier가 빈 이름을 변경한다고 생각할 수 있는데, 빈 이름은 그대로 있고 추가적으로 구분자가 생기는 것이다.

생성자 자동주입말고도 수정자 주입에서도 같은 방식으로 사용할 수 있다.

이 때 파라미터에 명시한 “dogdog” 구분자를 가진 빈이 없으면 dogdog이라는 이름을 가진 스프링 빈이 있는지를 추가적으로 탐색한다. 하지만 코드의 명확성이 떨어져 유지보수가 어려워질 수 있으니 빈 이름과 Qualifier는 별도로 관리하는 것이 좋다.

해당 이름을 가진 빈 마저 없을 경우 NoSuchBeanDefinitionException 예외가 발생한다.

@Component
@Qualifier("dogdog")
public class Dog implements Animal {
	public String sound() {
		return "왈왈";
	}
}
@Component
@Qualifier("catcat")
public class Cat implements Animal {
	public String sound() {
		return "야옹";
	}
}

@Service
public class AnimalService {
	private final Animal animal;
	@Autowired
	public AnimalService(@Qualifier("dogdog") Animal animal) {
			this.animal = animal;
	}
}

@Primary 어노테이션 사용

가장 간단한 방법으로 여러 빈이 있을 때 기본적으로 선택될 빈에 @Primary 어노테이션을 붙여주면 자동적으로 해당 빈이 선택된다. 주입 받을 때마다 모든 코드에 @Qualifier 어노테이션을 붙여줘야하는 Qualifier 방법보다 간단하다.

@Component
@Primary
public class Dog implements Animal {
	public String sound() {
		return "왈왈";
	}
}
@Component
public class Cat implements Animal {
	public String sound() {
		return "야옹";
	}
}

@Service
public class AnimalService {
	private final Animal animal;
	@Autowired
	public AnimalService(Animal animal) {
			this.animal = animal;
	}
}

@Primary와 @Qualifier를 같이 사용할 수도 있다. 메인 기능으로 사용되는 빈과 서브 기능으로 가끔 사용되는 빈이 있을 때 메인 기능으로 사용하는 빈에 @Primary를 적용하고, 서브 기능으로 사용하는 빈을 사용할 때는 @Qualifier를 명시적으로 지정하여 주입하면 두 어노테이션의 장점만을 각각 사용할 수 있다.

 

@Primary와 @Qualifier의 우선 순위

스프링은 기본적으로 자동보다 수동으로 지정한 것이 높은 우선 순위를 갖는다. 따라서 자동적으로 빈을 선택해주는 @Primary보다 명시적으로 지정하는 @Qualifier 어노테이션이 우선 순위를 가진다.

728x90
profile

개발바닥곰발바닥

@bestinu

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!