Spring Constructor DI (value 주입)

Constructor DI (value직접주입) 


생성자 주입에 관한 이전예제는 DI target이 bean으로 ref-bean 주입 방식이였다. 이번에는 String, int 등의 값을 직접 주입하는 방법이나, 생성자가 두 개 있는 경우 이를 어떻게 구분하는지에 대해서 알아보고자 한다. 이번 포스팅 부터 예제는 새로운 예제로 작성하여 가급적 전체 소스를 올리려고 한다. 



XML방법


<value> 태그 사용

  * bean을 주입하는 경우는 <ref bean="">를 사용

1
2
3
4
5
<bean id="messageProvider" class="com.ljs.message.provider.DefaultMessageProvider">
    <constructor-arg>
        <value>Spring Constructor Value DI Test</value>
    </constructor-arg>
</bean>    
cs

[Interface IMessageProvider]

1
2
3
4
5
package com.ljs.message.provider;
 
public interface IMessageProvider {
    public String getMessage();
}
cs


[class DefalutMessageProvider]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ljs.message.provider;
 
public class DefaultMessageProvider implements IMessageProvider {
    
    private String message;
    
    public DefaultMessageProvider(String message) {
        this.message = message;
    }
    
    @Override
    public String getMessage() {
        return this.message;
    }
}
cs


[class Main]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.ljs.message;
 
import org.springframework.context.support.GenericXmlApplicationContext;
 
import com.ljs.message.provider.IMessageProvider;
 
public class ApplicationMain {
 
    public static void main(String[] args) {
        // ApplicationContext 부트스트랩
        GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
        ctx.load("classpath:META-INF/spring/app-context-annotation.xml");
        ctx.refresh();
 
        // bean use
        IMessageProvider provider = (IMessageProvider) ctx.getBean("messageProvider");
        System.out.println(provider.getMessage());
 
        ctx.close();
    }
}
cs


[app-context-xml.xml]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
        
    <bean id="messageProvider" class="com.ljs.message.provider.DefaultMessageProvider">
        <constructor-arg>
            <value>Spring Constructor Value DI Test</value>
        </constructor-arg>
    </bean>    
</beans>
cs



Annotation방법

* 나머지 소스는 상동


생성자 파라메터에 @Autowired와 @Value 어노테이션 사용

  * Autowired 되어야 하기 때문에 아래 메소드가 정의된 클래스에는 @Service 등으로 bean등록이 되어야 함.

1
2
3
4
@Autowired
public DefaultMessageProvider(@Value("Spring Constructor Value DI Test") String message) {
    this.message = message;
}
cs

[class DefalutMessageProvider]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.ljs.message.provider;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
 
@Service("messageProvider")
public class DefaultMessageProvider implements IMessageProvider {
    
    private String message;
    
    @Autowired
    public DefaultMessageProvider(@Value("Spring Constructor Value DI Test") String message) {
        this.message = message;
    }
    
    @Override
    public String getMessage() {
        return this.message;
    }
}
cs


[app-context-annotation.xml]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
    
    
    <!--스프링이 코드기반에서 의존성 요구조건을 스캔하게끔 세팅-->    
    <context:annotation-config/>
    
    <!--스프링이 코드에서 지정된 패키지(및 하위)아래에 있는 주입 가능한 빈을 모두 스캔하도록 세팅-->
    <context:component-scan base-package="com.ljs.message"/>
    
</beans>
cs


여지까지 기본적은 constructor을 이용한 value 대입 방법이다. 


하지만 annotation방식에서는 소스코드에 하드코딩으로 메시지 내용이 포함되어 있어 추후 관리에 어려움이 예상된다. 또한 생성자가 두 개인 경우 어느 생성자로 Injection될 지 지정하는 방법을 아래에서 더 알아보자. 아래는 위에 소스를 기준으로 일부만 변경하도록 한다. 

 

 


Annotation방식에서 Value("Spring Constructor Value DI Test") 하드코딩 문제 해결

app-context-annotation.xml에 String bean 정의 

1
2
3
4
5
6
7
    <!--스프링이 코드기반에서 의존성 요구조건을 스캔하게끔 세팅-->    
    <context:annotation-config/>
    
    <!--스프링이 코드에서 지정된 패키지(및 하위)아래에 있는 주입 가능한 빈을 모두 스캔하도록 세팅-->
    <context:component-scan base-package="com.ljs.message"/>
    
    <bean id="message" class="java.lang.String" c:_0="Spring Constructor Value DI Test"/>
cs

* String 객체를 만들어 injection 한다. c:_0은 String객체를 만들 때 첫번째(0) 생성자로 "" 값을 넘겨준다는 의미



@Service로 등록된 클래스 @Autowired 된 생성자에 @Value 제거 

1
2
3
4
    @Autowired
    public DefaultMessageProvider( @Value("Spring Constructor Value DI Test") String message) {
        this.message = message;
    }
cs

위와같이 지정이 되면, @Service로 등록된 bean을 생성할 때, @Autowired  생성자에 파라메터로 bean(id="message")인 String 객체를 전달한다.

그리하여 소스코드에 있는 @Value는 더이상 불필요하고 소스와 메시지를 분리 할 수 있게 된다. 



생성자혼동


동일한 파라메터의 명이지만, 다른 타입으로 생성자가 생성되어 있는 경우 어디에 DI가 수행될지 판단이 어려울 때가 있다. 아래는 똑같은 message라는 파라메터를 받지만, 생성자 마다 String으로, int로 구현되어 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.ljs.message.provider;
 
public class DefaultMessageProvider implements IMessageProvider {
    
    private String message;
    
    public DefaultMessageProvider(String message) {
        this.message = message;
        System.out.println("String 생성자로 수행");
    }
    
    public DefaultMessageProvider(int message) {
        this.message = Integer.toString(message);
        System.out.println("int 생성자로 수행");
    }
    
    @Override
    public String getMessage() {
        return this.message;
    }
}
cs



생성자혼동(XML) 


기본적으로 xml에 정의된 <value>의 값은 String으로 인식한다. 그래서 위에 상황에서는 String message를 파라메터로 받는 생성자가 수행되게 된다. 

만약, int 파라메터를 받는 생성자를 수행하고자 한다면 아래와 <constructor-arg>에 타입을 지정하면 된다.


생성자 타입 명시적 지정 <constructor-arg type="int">

1
2
3
4
5
    <bean id="messageProvider" class="com.ljs.message.provider.DefaultMessageProvider">
        <constructor-arg type="int">
            <value>1000</value>
        </constructor-arg>
    </bean>    
cs\

생성자혼동(Annotation)


어노테이션 방식은 문제해결이 간단하다. @Autowired 어노테이션은 생성자 메서드 중 단 한곳에만 적용할 수 있다. 그렇지 않으면 ApplicationContext 부트스트랩 과정에서 에러가 발생한다. 


@Autowired는 생성자 중 한 곳에만 쓸 수 있다.

  - 그래서 생성자 혼동에 관련 된 혼동은 애초에 없다.

  - 사실, xml방식도 굳이 저렇게 구현하는 패턴은 없을 것 같지만

1
2
3
4
5
6
7
8
    public DefaultMessageProvider(String message) {
        this.message = message;
    }
    
    @Autowired
    public DefaultMessageProvider(@Value("100"int message) {
        this.message = Integer.toString(message);
    }     
cs

* 수행결과




이 글을 공유하기

댓글

Email by JB FACTORY