티스토리 뷰

SPRING/정리

의존관계 빈 설정 방법

란텔 2016. 8. 14. 15:30

XML에서 빈을 등록했다면 생성자나 자바빈 규약에 따른 수정자(set으로 시작하는 메서드)에 의존관계 빈이나 값을 주입할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
public class InjectionTest {
 
    public InjectionTest(String name, int age, String s) {
        System.out.println(name + "======" + age + "=========" + s.toString());
    }
 
    public void setParameterSetting(String a) {
        System.out.println(a);
    }
 
}
cs


1
2
3
4
5
6
7
8
9
<bean class="dev.wedding.kr.test.InjectionTest">
        <!-- 생성자에 값 주입 -->
        <constructor-arg index="0" value="StringValue" />
        <constructor-arg index="1" value="2016" />
        <constructor-arg index="2" value="java.lang.String" />
 
        <!-- set메서드(수정자)에 값을 주입 -->
        <property name="parameterSetting" value="choonie" />
</bean>
cs

스프링 컨텍스트 XML설정 파일에서 InjectionTest빈을 등록하고 있으며, 그 안에 생성자와 수정자 메서드가 필요로 하는 파라미터(매개변수)를 설정하고 있다.


우선 <constructor-arg>는 생성자의 매개변수에 객체나 값을 주입하는 것이다.

index속성은 생성자의 매개변수 수가 여러개일 때 설정하는 것으로 0부터 시작한다. value속성에는 값을 넣는다. 값이 기본값이 아닌 객체라면 value대신 ref를 써서 등록된 빈의 이름을 지정하면 된다.


<property>는 수정자 메서드 매개변수에 들어갈 값을 설정하는 것이다. 

name은 수정자 메서드 이름에서 set을 제외하고 첫글자를 소문자로 변경해야 한다.

역시 값은 value, 빈 참조는 ref속성을 사용한다.



1
2
3
4
5
<bean class="dev.wedding.kr.test.InjectionTest2">
        <constructor-arg type="java.lang.String" value="StringValue" />
        <constructor-arg type="dev.wedding.kr.model.CarInfoProvider"
            ref="kiaCar" />
</bean>
cs

생성자 각각의 파라미터에 중복되는 타입이 없다면 위와 같이 type속성 을 사용할 수 있다.






애노테이션을 이용해 빈의 의존관계를 정의할 수 있는 방법도 있다.

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class InjectionTest {
    private ConfigurationBeanFactory factory;
 
    @Resource(name = "configurationBeanFactory")
    public void setParameterSetting(ConfigurationBeanFactory factory) {
        this.factory = factory;
        CarInfoProvider cip = this.factory.kiaCar();
        cip.getPrintCarInfo();
    }
 
}
cs

@Resource를 사용함으로써 메서드의 매개변수에 의존관계 빈을 주입할 수 있다.

@Resource(name = "configurationBeanFactory")는 <property>에 대응 된다고 볼 수 있으며, XML에서 수정자 의존 주입을 설정할 때의 <property name="configurationBeanFactory" ref="configurationBeanFactory">와 같은 의미를 가진다.


그런데 이 같은 애노테이션들을 사용하기 위해서는 다음과 같은 방법중 하나를 사용해야 한다.


  • 스프링 컨텍스트 XML파일에 <context:annotation-config /> 등록하여 DI

<context:annotation-config />는 @Resource와 같은 애노테이션의 메타정보를 추가해주는 빈 후처리기를 등록해주는 전용 태그다. 쉽게 말하면 @Resource가 본연의 기능을 할 수 있도록 지원하는 것일 뿐 빈을 등록해 주지 않는다.

그래서 따로 <context:annotation-config />와 함께 필요한 빈을 등록해줘야 한다.

1
2
3
4
5
6
7
<context:annotation-config />
 
<bean id="configurationBeanFactory" class="dev.wedding.kr.test.ConfigurationBeanFactory">
</bean>
 
<bean class="dev.wedding.kr.test.InjectionTest">
</bean>
cs


  • 스프링 컨텍스트 XML파일에 <context:component-scan /> 등록하여 DI
1
<context:component-scan base-package="dev.wedding.kr.test" />
cs

<context:component-scan>만 사용하면 빈 스캐닝을 하고 빈 등록이 자동으로 이루어지기 때문에 첫번 째와 같은 방법을 사용하지 않아도 된다.

하지만 빈 스캐닝의 대상이 되기 위해 InjectionTest와 ConfigurationBeanFactory에 @Component를 붙여줘야 된다.

그리고 빈 스캐닝은 항상 애노테이션을 통한 의존관계주입을 지원한다.(@Resource등의 사용..)

  • AnnotationConfigApplicationContext를 사용해서 DI




1
2
3
4
5
6
7
8
@Component
public class InjectionTest {
 
    @Resource
    private ConfigurationBeanFactory configurationBeanFactory;
 
 
}
cs

@Resource애노테이션을 필드에도 사용할 수 있다. 수정자를 통하여 의존관계를 주입하는 것과 동일하다.

이 같은 의존관계 주입을 '필드 주입'이라고 한다.

하지만 이 방법은 스프링의 컨테이너 밖에서 테스트 할 때 불편하기 때문에 단위 테스트가 필요한 클래스라면 '필드 주입'보다는 수정자 주입을 사용하는 것이 좋다.


위의 @Resource는 name속성이 지정된 괄호가 포함되어 있지 않는데 이럴때는 등록된 빈의 이름과 위에서 보이는 configurationBeanFactory라는 변수의 이름과 일치하는 빈을 찾아서 의존성 주입을 한다.

만약 name속성을 지정하지 않고, 변수이름과 일치하는 빈을 찾을 수 없을 경우에는 타입명을 이용해 다시 한번 빈을 찾기도 한다. 

그러나 name속성을 뺀 @Resource의 사용은 권장하지 않는다. 그러니 name속성을 꼭 쓰자.





  • @Autowired

@Autowired는 XML의 타입에 의한 자동와이어링 방식을 생성자, 필드, 수정자(setXXX), 일반메서드에 대하여 지원한다.

이 애노테이션이 부여된 필드, 수정자 등을 만들어두면 스프링이 알아서 DI를 해준다.

@Resource와 다른점은 @Resource는 빈의 이름으로 찾지만 @Autowired는 선언된 타입과 일치하거나 관련있는 빈을 찾는다는 것이다.



  • @Autowired를 통한 의존관계 설정 방법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//필드 주입
@Component
public class InjectionTest {
    @Autowired
    private KiaCar kia;
}
 
 
 
 
//수정자 프로퍼티 주입
@Component
public class InjectionTest {
    private HyundaiCar hyundai;    
    
    @Autowired
    public void setHyundaiCar(HyundaiCar hyundai){
        this.hyundai = hyundai;
    }
}
 
 
 
//생성자 주입
@Component
public class InjectionTest {
    private KiaCar kia;
    private HyundaiCar hyundai;
    
    
    @Autowired
    public InjectionTest(KiaCar kia, HyundaiCar hyundai){
        this.kia = kia;
        this.hyundai = hyundai;
    }
}
 
 
 
//일반 메서드 주입
@Component
public class InjectionTest {
    private KiaCar kia;
    private HyundaiCar hyundai;
 
    
    @Autowired
    public void config(KiaCar kia, HyundaiCar hyundai){
        this.kia = kia;
        this.hyundai = hyundai;
    }
}
cs

root-context.xml

1
2
3
<bean class="dev.carworld.kr.model.KiaCar"></bean>
    
<bean class="dev.carworld.kr.model.HyundaiCar"></bean>
cs

필드 주입 방식에서는 필드의 타입에 맞는 빈을 스프링 xml 설정에서 찾는다.

수정자, 생성자, 일반메서드 주입방식에서는 매개변수의 타입에 맞는 빈을 스프링 xml 설정에서 찾는다.


@Autowired는 단 하나의 생성자에만 사용할 수 있다.


수정자 방식은 DI해줘야 할 프로퍼티가 늘어날 때마다 수정자를 계속 추가(매개변수가 하나이기 때문)해야 하는 불편 때문에 한번에 필드에 DI해줄 수 있는 생성자 방식을 사용하지만 생성자 방식도 컨테이너 초기화 작업 중에 DI가 되지 않아 에러가 발생할 수 있다는 단점이 있다. 또한 @Autowired로 설정할 수 있는 생성자는 단 하나뿐이다.


하지만 일반 메서드 주입 방식을 이용하면 생성자 방식과는 달리 여러 메서드에 @Autowired를 사용할 수 있고, 수정자 방식처럼 1개의 매개변수만 갖는 것과 달리 여러개의 매개변수를 두어 DI시킬 수 있다.

그러나 일반 메서드 주입방식은 xml을 통하여 의존관계를 설정할 수 없다.




@Autowired를 이용하면 같은 타입의 빈이 하나 이상 존재할 때 그 빈들을 모두 DI받을 수 있다.

필드, 수정자, 일반 메서드 주입방식에서 타입을 배열이나 컬렉션으로 선언하면 된다.


CarInfoProvider라는 인터페이스가 KiaCar와 HyundaiCar라는 클래스에 구현되었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//Collection으로 DI받기
@Autowired
Collection<CarInfoProvider> info;
 
//List로 DI받기
@Autowired
List<CarInfoProvider> info;
 
//배열로 DI받기
@Autowired
CarInfoProvider[] info;
 
 
//수정자 메서드의 매개변수를 Set으로 설정 후 DI
Set<CarInfoProvider> info; 
 
@Autowired
public void setCarInfo(Set<CarInfoProvider> info){
    this.info = info;
}
cs

위와 같은 방식으로 의존관계 빈을 주입하면 CarInfoProvider가 구현한 빈을 모두 주입 받을 수 있다. 





  • @Qualifier

Qualifier는 타입 외의 정보를 빈 등록시 추가해서 자동와이어링을 보다 정확하게 제어할 수 있는 보조적 방법이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface DataAccessProvider {
    public void connection();
}
 
 
public class MysqlDatabase implements DataAccessProvider{
    public void connection(){
        System.out.println("MySQL에 연결되었습니다.");
    }
}
 
 
public class OracleDatabase implements DataAccessProvider{
    public void connection(){
        System.out.println("Oracle에 연결되었습니다.");
    }
}
 
cs


1
2
<bean id="mysql" class="dev.wedding.kr.persis.MysqlDatabase"></bean>
<bean id="oracle" class="dev.wedding.kr.persis.OracleDatabase"></bean>
cs

위와 같이 스프링 xml 설정에 빈 등록을 했을 때 @Resource(name="mysql")과 같이 사용하면 문제가 없으나 @Autowired같이 자동와이어링틀 통해서 빈을 주입할 때는 문제가 발생한다.

자동와이어링 방식은 관계된 타입을 찾아서 빈을 주입하는데 위의 빈 타입은 둘 다 Database이기 때문에 스프링이 이 2개의 빈 중에 어떤 것을 DI할지 판단할 수 없기 때문이다.



1
2
3
4
5
6
7
8
9
10
11
//정상
@Resource(name="oracle")
public void setDatabase(DataAccessProvider data){
    data.connection();
}
 
//에러
@Autowired
public void setDatabase(DataAccessProvider data){
    data.connection();
}
cs

이런 경우 @Resource만을 사용하는 것이 해결책일 수 있으나, 꼭 @Autowired를 사용해야 할 경우 @Qualifier를 같이 사용하면 해결이 가능하다.



@Qualifier는 해당 빈에 기술되어 있는 <qualifier>태그를 참고한다.


1
2
3
4
<bean id="mysql" class="dev.wedding.kr.persis.MysqlDatabase"></bean>
<bean id="oracle" class="dev.wedding.kr.persis.OracleDatabase">
    <qualifier value="useDB"></qualifier>
</bean>
cs


그리고 의존관계 설정을 하는 자바코드에서는


1
2
3
4
5
@Autowired
@Qualifier("useDB")
public void setDatabase(DataAccessProvider data){
    data.connection();
}
cs

이와 같이 하면 @Qualifier에 지정된 문자열을 참고하여 등록된 빈에서 이 문자열과 일치하는 <qualifier>태그를 찾아 그 빈을 주입시킨다.



그런데 아래와 같이 빈 등록을 XML에서 하지 않고 @Component같이 스테레오 타입으로 자바 코드 상에서 빈 스캔대상이 되게 한다면 <qualifier>태그를 사용할 수 없기 때문에 XML로는 설정할 방법이 없다

1
2
3
4
5
6
@Component
public class MysqlDatabase implements DataAccessProvider{
    public void connection(){
        System.out.println("MySQL에 연결되었습니다.");
    }
}
cs


이럴 때는 다음과 같이 해주면 된다.


1
2
3
4
5
6
7
@Component
@Qualifier("useDB"//<qualifier value="useDB" />와 같다.
public class MysqlDatabase implements DataAccessProvider{
    public void connection(){
        System.out.println("MySQL에 연결되었습니다.");
    }
}
cs




@Qualifier가 빈을 부여할 수 있는 대상은 필드(멤버변수), 수정자(setXX메서드), 매개변수이다.

그래서 생성자 주입과 일반 메서드 주입에는 @Qualifier를 붙여도 소용이 없다. 

@Qualifier를 통해 생성자와 메서드에 의존관계 빈 주입을 하려면 각각의 매개변수 마다 @Qualifier를 붙여야 한다.

1
2
3
4
5
6
7
@Component
public class InjectionTest {
    @Autowired
    public InjectionTest(@Qualifier("useDB") DataAccessProvider data){
        data.connection();
    }
}
cs


Comments
최근에 올라온 글
최근에 달린 댓글
TAG
more
Total
Today
Yesterday