[Spring] 스프링(Spring) @Qualifier, @Named, @Primary 의존객체 선택

2020. 12. 17. 18:10 Spring Framework/Spring 입문 - 개념 및 핵심

| 스프링 의존객체 선택(Select Spring Dependency Object)


@Autowired, @Resource, @Inject 어노테이션으로만 자동적으로 객체를 주입할 경우 컨테이너에서 주입할 대상이 여러개여서 의존성을 주입하지 못하는 경우가 발생할 수 있다.


다음과 같이 @Autowired를 통해 객체를 주입하려고 할 때, 컨테이너상에서는 Weapon 인터페이스를 상속한 2개의 bean 객체인 gun과 knife가 존재한다. @Autowired는 먼저 Type을 기준으로 주입할 객체를 정하기 때문에 같은 Type이 스프링 컨테이너 상에 존재할 경우 에러를 내게 된다.


appContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<context:annotation-config/>

<bean id="gun" class="com.tutorial.spring.Gun"/>
<bean id="knife" class="com.tutorial.spring.Knife"/>

<bean id="gunPlayer" class="com.tutorial.spring.Player"/>

<bean id="gunPlayer2" class="com.tutorial.spring.Player2"/>
</beans>
public class Player {

@Autowired
private Weapon weapon;

Player(){
}

public Player(Weapon weapon) {
super();
this.weapon = weapon;
}

public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}

public Weapon getWeapon() {
return weapon;
}

public void usePlayerWeapon() {
weapon.useWeapon();
}

public void setPlayerWeaponNow(Weapon weapon) {

}
}

// 결과
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'gunPlayer': Injection of autowired dependencies failed; nested exception is ...
... (생략 ) ...

이 때, @Qualifier나 @Named 어노테이션을 써서 하나의 의존객체를 선택하여 객체 간 선택적 충돌이 발생하지 않게 할 수 있다.

@Qualifier

@Qualifier는 저번 포스팅에서도 잠시 언급한 적이 있다. @Qualifier는 @Autowired와 같이 쓰이며 여러개의 타입이 일치하는 bean객체가 있을 경우 @Qualifier 어노테이션의 유무를 확인한 후 조건에 만족하는 객체를 주입하게 된다. 

appContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<context:annotation-config/>

<bean id="gun" class="com.tutorial.spring.Gun">
<qualifier value="gunner"></qualifier> <-- Qualifier를 명시한다 -->
</bean>
<bean id="knife" class="com.tutorial.spring.Knife"/>

<bean id="gunPlayer" class="com.tutorial.spring.Player"/>

<bean id="gunPlayer2" class="com.tutorial.spring.Player2"/>
</beans>
import javax.inject.Inject;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class Player {

@Autowired
@Qualifier("gunner") // appContext.xml 에 명시했던 qulifier name을 쓴다
private Weapon weapon;

Player(){
}

public Player(Weapon weapon) {
super();
this.weapon = weapon;
}

public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}

public Weapon getWeapon() {
return weapon;
}

public void usePlayerWeapon() {
weapon.useWeapon();
}

public void setPlayerWeaponNow(Weapon weapon) {

}
}

public class Main {

public static void main(String[] args) {
String xmlConfigPath = "classpath:appContext.xml";

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(xmlConfigPath);

Player gunPlayer = ctx.getBean("gunPlayer", Player.class);
gunPlayer.usePlayerWeapon();

}
}


@Named

@Named는 @Inject와 함께 쓰이며 @Qualifier와 달리 xml 컨테이너 설정파일에 다른 정보를 명시할 필요 없이 기존의 bean객체의 ID를 가지고 객체를 명시적으로 선택하게 된다.

appContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<context:annotation-config/>

<bean id="gun" class="com.tutorial.spring.Gun">
</bean>
<bean id="knife" class="com.tutorial.spring.Knife"/>

<bean id="gunPlayer" class="com.tutorial.spring.Player"/>

<bean id="gunPlayer2" class="com.tutorial.spring.Player2"/>
</beans>
package com.tutorial.spring;

import javax.inject.Inject;
import javax.inject.Named;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.support.GenericXmlApplicationContext;

public class Player {

@Inject
@Named("gun") // 스프링 컨테이너 xml 설정파일에 명시되어 있는 ID를 적음
private Weapon weapon;

Player(){
}

public Player(Weapon weapon) {
super();
this.weapon = weapon;
}

public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}

public Weapon getWeapon() {
return weapon;
}

public void usePlayerWeapon() {
weapon.useWeapon();
}

public void setPlayerWeaponNow(Weapon weapon) {

}
}

public class Main {

public static void main(String[] args) {
String xmlConfigPath = "classpath:appContext.xml";

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(xmlConfigPath);

Player gunPlayer = ctx.getBean("gunPlayer", Player.class);
gunPlayer.usePlayerWeapon();

}
}


@Primary

@Primary로 같은 우선순위로 있는 클래스가 여러개가 있을 시 그 중 가장 우선순위로 주입할 클래스 타입을 선택할 수 있다.
@Repository @Primary
public class BSRepository implements BookRepository {
}



출처: https://engkimbs.tistory.com/683?category=767795 [새로비]