Back Ground

DI(의존성 주입) 본문

Spring

DI(의존성 주입)

Back 2016. 11. 23. 19:37



스프링 프레임워크의 가장 중요한 특징은 객체의 생성과 의존관계를 컨테이너가 자동으로 관리 한다는 점이다.

이 것이 바로 스프링 IoC(제어의 역행이다)의 핵심 원리이기도 하다. 스프링은 IoC를 다음 두가지 형태로 지원한다.


- Dependency Lookup

- Denpndency Injection 



 

Inversion of Control

 

 

┌---------------

-------------┴-----------

-----------------┐

 

 Dependency Lookup

 

 Dependency Injection

 

 

┌-----------------

------------┴------------

-------------------┐ 

 

 Setter Injection

 

 Constructor Injection




이 중에서 컨테이너가 애플리케이션 운용에 필요한 객체를 생성하고 

클라이언트는 컨테이너가 생성한 객체를 검색(Lookup)하여 사용하는 방식을 Dependency Lookup이라고 한다.

하지만 Dependency Lookup은 실제 애플리케이션 개발 과정에서는 사용하지 않으며, 


대부분 Dependency Injection을 사용하여 개발한다


Dependency Injection은 객체 사이의존 관계스프링 설정 파일에 등록된 정보( <bean> )를 

바탕으로 컨테이너가 자동으로 처리해 준다. 따라서 의존성 설정을 바꾸고 싶을때 프로그램코드를 수정하지 않고

스프링 설정 파일 수정만으로 변경 사항을 적용할 수 있어서 유지보수가 향상된다. 


Dependency Injection은 컨테이너가 직접 객체들 사이에 의존관계를 처리하는 것을 의미하며,


이것은 다시

▷ setter 메소드를 기반으로 하는 세터 인젝션(Setter Injection)과

▷ 생성자를 기반으로 하는 생성자 인젝션(Constructor Injection)으로 나뉜다.





DI(Dependency Injection)

의존성 주입


우리나라 번역으로 의존성 주입이라 한다. 

하지만 의존성이라는 말은 DI의 의미가 무엇인지 잘 드러내주지 못한다.


또한 의존(종속) 오브젝트 주입이라고도 부르기도 하는데, 

이때는 DI가 일어나는 방법에 초점을 맞춘 것이다.

엄밀히 말해서 오브젝트는 다른 오브젝트에 주입할 수 있는게 아니다.

오브젝트의 레퍼런스(참고되는 대상)가 전달될 뿐이다. 


DI는 오브젝트 레퍼런스를 외부로부터 제공(주입)받고 

이를 통해여 타 오브젝트와 다이나믹하게 의존관계가 만들어지는 것이 핵심이다.


용어는 동작방식(메커니즘)보다는 의도를 가지고 이름을 짓는 것이 좋다.

그런면에서 의존관계 주입이라는 번역이 적절할 듯 싶고, 이 책에서는 이를 사용한다.


하지만 DI가 무엇인지만 잘 인식하고 있다면 어떤 용어를 사용해도 상관없다.

의도를 강하게 드러내는 '의존관계 설정'이라는 용어도 나쁘지않다고 생각한다.





의존관계


먼저 의존관계란 무엇인지 생각해보자.

두 개의 클래스 또는 모듈이 의존관계에 있다고 말할 때는 항상 방향성을 부여해줘야 한다.

즉,  누가 누구에게 의존하는 관계에 있다는 식이어야 한다.




UML 모델에서는 이렇게

두 클래스의 의존관계(dependency relationship)를 점선으로 된 화살표로 표현한다.


그렇다면 의존하고 있다는 건 무슨 의미일까? 의존한다는건  의존대상, 여기서는

B가 변하면 그것이 A에 영향을 미친다는 뜻이다.

B의 기능이 추가되거나 변경되거나, 형식이 바뀌거나 하면 그 영향이 A로 전달된다는 것이다.


대표적인 예는

A가 B를 사용하는 경우, 예를 들어 A에서 B에 정의된 메소드를 호출해서 사용하는 경우다.

이럴 땐 '사용에 대한 의존관계'가 있다고 말할 수 있다.


만약 B에 새로운 메소드가 추가 되거나 기존 메소드의 형식이 바뀌면

A도 그에 따라 수정되거나 추가돼야 할 것이다.


또는 B의 형식은 그대로지만 기능이 내부적으로 변경되면,

결과적으로 A의 기능이 수행되는 데도 영향을 미칠 수 있다.

이렇게 사용의 관계에 있는 경우에 A와 B는 의존 관계가 있다고 말할 수 있다.

 

 다시말하지만 의존 관계에는 방향성이 있다. A가 B에 의존하고 있지만, 반대로 B는 A에 의존하지 않는다.

의존하지 않는다는 말은 B는 A의 변화에 영향을 받지 않는다는 뜻이다.




의존성(Denpendeny) 관계란 객체와 객체의 결합관계이다.

즉 , 하나의 객체에서 다른 객체의 변수나 메소드를

이용해야 한다면 이용하려는 객체에 대한 객체 생성과 새성된 객체의 레퍼런스정보가 필요하다






예제에서 사용할 클래스 구성


ⓘ polymotphism.TV

 ⊙ powerOn():void

 ⊙ powerOff():void

 ⊙ powerUp():void

 ⊙ volumeDown():void



 ⓒ polymirphism.SonySpeaker

 ⊙ SonySpeaker()

 ⊙ volumeUp():void

 ⊙ volumeDown():vodi




 

 

 

 

ⓒ polymorphism.SamsungTV

Θ speaker.SonySpeaker

 ⊙ SamsungTV()

 ⊙ powerOn():void

 ⊙ powerOff():void

 ⊙ volumeUp():void

 ⊙ volumeDown():void


 

 


SamsungTV는 SonySpeaker의 메소드를 이용해서 기능을 수행한다.

따라서 SamsungTV는 SonySpeaker타입의 speaker 변수를 멤버변수로 가지고 있으며,

speaker변수는 SonySpeaker 객체를 참조하고 있어야 한다.

따라서 SamsungTV 클래스 어딘가에는 SonySpeaker 클래스에 대한 객체 생성 코드가 반드시 필요하다.



SonySpeaker.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package polymorphism;
public class SonySpeaker{
    public SonySpeaker(){
        System.out.println("===> SonySpeaker 객체 생성");
    }
    public void volumeUp(){
        System.out.println("SonySpeaker---소리 올린다.");
    }        
    public void volumeDown(){
        System.out.println("SonySpeaker---소리 내린다.");
    }
}
 




SamsungTV 클래스의 볼륨 조절 기능을 SonySpeaker가 이용하도록 수정한다.



SamsungTV.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package polymorphism;
public class SamsungTV implements TV{
    private SonySpeaker speaker;
    public SamsungTV(){
        System.out.println("===> SonySpeaker 객체 생성");
    }
    public void powerOff(){
        System.out.println("SamsungTV---전원 끈다.");
    }        
    public void volumeUp(){
        speaker = new SonySpeaker();
        speaker.volumeUp();
    }
    public void volumeDown(){
        speaker = new SonySpeaker();
        speaker.volumeDown();
    }
}
 




클라이언트가 volumeUp()과 volumeDown() 중 어떤 메소드를 먼저 호출할지 모르기 때문에

두 메소드에 SonySpeaker 객체 생성 코드를 모두 작성했다.

이제 TVUser 클라이언트 프로그램을 실행하면 다음과 같이 잘 실행된다.




<실행결과>

SamsungTV---전원 켠다.

===> SonySpeaker 객체 생성

SontSpeaker---소리 올린다.

===> SonySpeaker 객체 생성 

SonySpeaker---소리 내린다.

SamsungTV---전원 끈다.




하지만 이 프로그램에는 두 가지 문제가 있다.


첫 번째는 SonySpeaker 객체가 쓸데 없이 두 개나 생성되는 것이고,

두 번째는 운영 과정에서 SonySpeaker의 성능이 떨어져서

SonySpeaker를 AppleSpeaker 같은 다른 Speaker로 변경하고자 할 때,

volumeUp(), volumeDown()두 개의 메소드를 모두 수정해야 한다.


이러한 문제가 발생하는 이유는 의존관계에 있는

Speaker객체에 대한 객체 생성 코드를 직접 SamsungTV소스에 명시했기 때문이다.


스프링은 이 문제를 의존성 주입(Dependency Injection)을 이용하여 해결한다.







생성자 인젝션(주입) 이용하기



스프링 컨테이너는 XML설정 파일에 등록된 클래스를 찾아서 객체 생성할 때

기본적으로 매개변수가 없는 기본(Default) 생성자를 호출한다.


하지만 컨테이너가 기본 생성자 말고 매개변수를 가지는 다른 생성자를 호출하도록 설정할 수 있는데,

이 기능을 이용하여 생성자 인젝션(Constructor Injection)을 처리한다.

생성자 인젝션을 사용하면 생성자의 매개변수로 의존관계에 있는 객체의 주소 정보를 전달할 수 있다.


생성자 인젝션을 테스트하기 위해서 SamsungTV 클래스에 생성자를 추가한다.



SamsungTV.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package polymorphism;
public class SamsungTV implements TV{
    private SonySpeaker speaker;
 
    public SamsungTV(){
        System.out.println("===> SonySpeaker 객체 생성");
    }
    public SamsungTV(SonySpeaker speaker){
        System.out.println("===> SamsungTV(2) 객체 생성");
        this.speaker = speaker;
    }
    public void powerOn(){
        System.out.println("SamsungTV---전원 켠다.")
    }
    public void powerOff(){
        System.out.println("SamsungTV---전원 끈다.");
    }        
    public void volumeUp(){
        speaker.volumeUp();
    }
    public void volumeDown(){
        speaker.volumeDown();
    }
}
cs

SamsungTV 클래스에 추가된 두 번째 생성자는 SonySpeaker 객체를

매개변수로 받아서 멤버변수로 선언된 speaker를 초기화한다.


이를 위한 XML 설정 파일은 다음과 같다.



applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <bean id="tv" class="polymorphism.SamsungTV">
     <constructor-arg ref="sony" ></constructor-arg>
    </bean> ↙
    <bean  id="sony"  class="polymorphism.SonySpeaker"></bean>
</beans>


생성자 인젝션을 위해서는 SamsungTV 클래스 <bean> 등록 설정에서

시작 태그와 종료태그 사이에 <constructor-arg> 엘리먼트를 추가하면 된다.


그리고 생성자 인자로 전달할 객체의 아이디를 <constructor-arg> 엘리먼트에 ref속성으로 참조한다.


실행 결과는 다음과 같은데, 실행 결과를 통해서 두 가지 사실을 확인하기 바란다.


첫 번째는 SamsungTV 클래스의 개게가 생성될 때,

기본 생성자가 아닌 두 번째 생성자가 사용됐다는 점이고,


두번째는 스프링 설정파일에 SonySpeaker가 SamsungTV클래스 밑에 등록되었는데도 먼저 생성되고 있다는 점이다.



<실행결과>

===> SontSpeaker 객체 생성

===> SamsungTV(2) 객체 생성

SamsungTV---전원 켠다.

SonySpeaker---소리 올린다.

SonySpeaker---소리 내린다.

SamsungTV---전원 끈다.


스프링 컨테이너는 기본적으로 bean 등록된 순서대로 객체를 생성하며,

모든 객체는 기본생성자 호출을 원칙으로 한다.


그런데 생성자 인젝션으로 의존성 주입될 SonySpeaker가 먼저개체 생성되었으며,

SonySpeaker 객체를 매개변수로 받아들이는 생성자를 호출하여 객체 를 생성하였다.

결국 SamsungTV는 SonySpeaker객체를 참조할 수 있으므로 문제없이 볼륨 조절을 처리할 수 있게 되었다.








다중 변수 매핑


생성자 인젝션에서 초기해야 할 멤버변수가 여러개이면, 여러개의값을 한꺼번에 전달해야 한다.


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
package polymorphism;
public class SamsungTV implements TV{
    private SonySpeaker speaker;
    preivate int price;
 
    public SamsungTV(){
        System.out.println("===> SamsungTV(1) 객체 생성");
    }
    public SamsungTV(SonySpeaker speaker){
        System.out.println("===> SamsungTV(2) 객체 생성");
        this.speaker = speaker;
    }
 
    public SamsungTV(SonySpeaker Speaker, int price){
        System.out.printn("===> SamsungTV(3) 객체 생성");
        this.price = price;
    }
    public void powerOn(){
        System.out.println("SamsungTV---전원 켠다. (가격:)"+price+")");
    }
    public void powerOff(){
        System.out.println("SamsungTV---전원 끈다.");
    }        
    public void volumeUp(){
        speaker.volumeUp();
    }
    public void volumeDown(){
        speaker.volumeDown();
    }
}



이 때는 이렇게 생성자를 적절하게 추가하면 된다.





그리고 스프링 설정 파일에 <constructor-arg> 엘리먼트를 매개변수의 개수만큼 추가해야 한다.

(매개변수 순서대로 맞춰야 한다)

applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <bean id="tv" class="polymorphism.SamsungTV">
        <constructor-arg ref="sony"></constructor-arg> ←(1번째인자 speaker)
        <constructor-arg value="27000000"></constructor-arg> ←(2번째인자 price)
    </bean>
    <bean id="sony" class="polymorphism.SonySpeaker"></bean>
</beans>
cs


<consturctor-arg> 엘리먼트에는 ref와 value 속성을 사용하여 생성자 매개변수로 전달할 값을 지정할 수 있다.


이때 인자로 전달될 데이터가 <bean>으로 등록된 다른 객체일 때는

ref속성을 이용하여 해당 객체의 아이디나 이름을 참조하지만,


고정된 문자열이나 정수 같은 기본형 데이터일때는 value속성을 사용한다.


따라서 SonySpeaker 객체를 생성자 인자로 전달할때는 ref속성을 이용했지만,

2700000이라는 정수값을 전달할 때는 value 속성을 사용하였다.



TVUser 프로그램을 실행한 결과

<실행결과>

===> SonySpeaker 객체 생성

===> SamsungTV(3) 객체 생성

 SamsungTV---전원 켠다.   (가격 : 2700000)

 SonySpeaker---소리 올린다.

 SonySpeaker---소리 내린다.

 SamsungTV---전원 끈다.




그런데 생성자가 여러개 오버로딩 되어있다면 어떤 생성자를 호출해야 할지 분명하지 않을 수 있다.


이를 위해 index 속성을 지원하며,

index속성을 이용하면 어떤 값이 몇 번째 매개변수로 매핑되는지 지정 할 수 있다.

index는 0부터 시작한다.


applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <bean id="tv" class="polymorphism.SamsungTV">
        <constructor-arg index="0" ref="sony"></constructor-arg>
        <constructor-arg index="1" value="27000000"></constructor-arg>
    </bean>
    <bean id="sony" class="polymorphism.SonySpeaker"></bean>
</beans>
cs





의존관계 변경


지금까지는 SamsungTV 객체가 SonySpeaker를 이용하여 동작했지만

유지보수 과정에서 다른 스피커로 교체하는 상황도 발생 할  것이다.


의존성 주입은 이런 상황을 매우 효과적으로 처리 해준다.


실습을 위해 모든 스피커의 최상위 부모로 사용할 Speaker 인터페이스를 추가한다.


Speaker.java

1
2
3
4
5
6
package polymorphism;
 
public interface Speaker {
    void volumeup();
    void volumeDown();
}
cs

그리고 Spaeker 인터페이스를 구현한 또 다른 스피커인 AppleSpeaker를 추가로 구현한다.



AppleSpeaker.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package polymorphism;
 
public class AppleSpeaker implements Speaker{
    public AppleSpeaker(){
        System.out.println("===> AppleSpeaker 객체 생성");
    }
    public void volumeUp(){
        System.out.println("AppleSpeaker---소리 울린다.");
    }
    public void volumeDown(){
        System.out.println("AppleSpeaker---소리 내린다.");
    }
}
 



기전의 SonySpeaker 클래스 역시 Speaker 인터페이스를 implements 하도록 수정해야 한다.

물론 SonySpeaker 클래스를 이용하여 Speaker 인터페이스를 자동 생성했다면 자동으로

implements가 추가 되어 있을것이다.


1
2
3
4
package polymorphism;
 
public class SonySpeaker implemnts Speaker{
}




그리고 나서 SamsungTV클래스의 멤버변수와 매개변수 타입을 모두 Speaker로 수정하면 된다.





SamsungTV.java

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
package polymorphism;
public class SamsungTV implements TV{
    private Speaker speaker/* private SonySpeaker speaker; */
    preivate int price;
 
    public SamsungTV(){
        System.out.println("===> SamsungTV(1) 객체 생성됨");
    }
    public SamsungTV(Speaker speaker){ /* SonySpeaker speaker */
        System.out.println("===> SamsungTV(2) 객체 생성됨");
        this.speaker = speaker;
    }
 
    public SamsungTV(Speaker Speaker, int price){ /* SonySpeaker Speaker, int price */
        System.out.printn("===> SamsungTV(3) 객체 생성됨");
        this.price = price;
    }
    public void powerOn(){
        System.out.println("SamsungTV---전원 켠다. (가격:)"+price+")");
    }
    public void powerOff(){
        System.out.println("SamsungTV---전원 끈다.");
    }        
    public void volumeUp(){
        speaker.volumeUp();
    }
    public void volumeDown(){
        speaker.volumeDown();
    }
}
cs



마지막으로 AppleSpeaker도 스프링 설정 파일에 <bean> 등록하고,

<contructor-arg>엘리먼트의 속성값을 apple로 지정하면 SamsungTV가 AppleSpeaker를 이용하여 볼륨을 조절한다.



applicationContet.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <bean id="tv" class="polymorphism.SamsungTV">
        <constructor-arg ref="apple"> </constructor-arg>
        <constructor-argvalue="27000000"></constructor-arg>
    </bean>
    <bean id="sony"  class="polymorphism.SonySpeaker"></bean>
    <bean  id="apple" class="polymorphism.AppleSpeaker"></bean>
</beans>

cs



XML 실행 기준
①<constructor-arg ref="apple"></constructor-arg>


   <bean  id="apple" class="polymorphism.AppleSpeaker"></bean>

- 객체화 한 것이 통째로 넘어간다.


②<bean id="tv" class="polymorphism.SamsungTV">


<bean id="sony" class="polymorphism.SonySpeaker"></bean>

- 이것은 메인에서 xml 호출을 먼저 할 경우


<constructor-arg value="27000000"></constructor-arg>

- 일단 3번째 순서로 값은 들어갔는데

   xml이 가장 먼저 실행되어 sony가 실행된 후 samsungTV에 가격이 후에 뜬다





TVUser 프로그램을 다시 실행하면 다음과 같이 실행되는 Speaker가 AppleSpeaker로 변경된 것을 확인 할 수 있다.

<실행결과>

===> AppleSpeaker 객체 생성

===> SamsungTV(3) 객체 생성

===> SonySpeaker 객체 생성

SamsungTV---전원 켠다. (가격: 2700000)

AppleSpeaker---소리 올린다.

AppleSpeaker---소리 내린다.

SamsungTV---전원 끈다.


결국 스프링 설정 파일만 적절히 관리하면 동작하는 TV도 변경할 수 있고,

TV가 사용하는 스피커도 변경할 수 있다.

여기에서 핵심은 이 과정에서 어떤 자바 코드도 변경하지 않는다는 것이다.







Setter 인젝션 이용하기


생성자 인젝션은 생성자를 이용하여 의존성을 처리한다.


하지만 setter 인젝션은 이름에서 알 수 있듯이 Setter 메소드를 호출하여 의존성 주입을 처리하는 방법이다.

두 가지 방법 모두 멤버변수를 원하는 값으로 설정하는 것을 목적으로 하고 있고, 결과가 같으므로 둘 중 어떤 방법을 쓰든 상관없다.


다만 코딩 컨벤션에 따라 한 가지로 통일해서 사용하는데 대부분은 Setter 인젝션을 사용하며,

Setter 메소드가 제공되지 않는 클래스에 대해서 생성자 인젝션 사용한다.




SamsungTV.java

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
package polymorphism;
 
public class SamsungTV implements TV{
    private Speaker speaker;
    preivate int price;
 
    public SamsungTV(){
        System.out.println("===> SonySpeaker(1) 객체 생성");
    }
    public void setSpeaker(Speaker speaker){ 
        System.out.println("===> setSpeaker()호출");
        this.speaker = speaker;
    }
 
    public void setPrice(int price){
        System.out.printn("===> setPrice() 호출");
        this.price = price;
    }
    public void powerOn(){
        System.out.println("SamsungTV---전원 켠다. (가격:)"+price+")");
    }
    public void powerOff(){
        System.out.println("SamsungTV---전원 끈다.");
    }        
    public void volumeUp(){
        speaker.volumeUp();
    }
    public void volumeDown(){
        speaker.volumeDown();
    }
}
cs


Setter 메소드는 스프링 컨테이너가 자동으로 호출하며,  호출하는 시점은 <bean>객체 생성 직후이다


따라서 Setter 인젝션이 동작하려면 Setter 메소드뿐만 아니라 기본 생성자도 반드시 필요하다



Setter 인젝션을 이용하려면 스프링 설정 파일에 <constructor-arg> 엘리먼트 대신 <property>엘리먼트를 사용해야 한다.





applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <bean id="tv" class="polymorphism.SamsungTV">
        <property name="spaker" ref="apple"></property>
        <property name="price" value="27000000"></property>
    </bean>
    <bean id="sony" class="polymorphism.SonySpeaker"></bean>
    <bean id="apple" class="polymorphism.AppleSpeaker"></bean>
</beans>





Setter인젝션을 이용하려면 <property> 엘리먼트를 사용해야 하며 name 속성값이 호출하고자 하는 메소드 이름이다.


즉, name 속성값이 "speaker"라고 설정되어 있으면 호출되는 메소드 setSpeaker()이다.


<property name="spaker" ref="apple"></property>


setSpeaker()

<property name="price" value="27000000"></property>


setPrice()


변수이름에서 첫 글자를 대문자로 바꾸고 앞에 "set"을 붙인 것이 호출할 메소드 이름이다.



<실행결과>

===> SamsungTV(1) 객체 생성

===> Applespeaker 객체 생성

===> setSpeaker() 호출

===> setPrice() 호출

===> sonySpeaker 객체 생성

SamsungTV---전원 켠다. (가격 : 2700000)

AppleSpeaker---소리 올린다.

AppleSpeaker---소리 내린다.

SamsungTV---전원 끈다.

 







p 네임스페이스 사용하기



Setter 인젝션을 설정할때, 'p 네임스페이스'를 이용하면 좀 더 효율적으로 의존성 주입을 처리 할 수 있다.

p 네임스페이스는 네임스페이스에 대한 별도의 schemaLocation이 없다.


따라서 네임스페이스만 적절히 선언하고 사용할 수 있다.


applicationContext.xml

1
2
3
4
5
6
7
8
<?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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
cs


p 네임스페이스를 선언했으면 다음과 같이


참조형 변수에 참조할 객체를 할당할 수 있다.

p:변수명-ref="참조할 객체의 이름이나 아이디"



기본형이나 문자형 변수에 직접 값을 설정할 때는 이렇게 사용

p:변수명="설정할 값"




다음은 p 네임스페이스를 이용하여 SamsungTV와 SonySpeaker의 의존성 주입을 설정한 것이다.


applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <bean id="tv" class="polymorphism.SamsungTV" p:speaker-ref="sony" p:price="2700000">
            
    <bean id="sony" class="polymorphism.SonySpeaker"></bean>
    <bean id="apple" class="polymorphism.AppleSpeaker"></bean>
</beans>
cs







컬렉션(Collection) 객체 설정


배열이나 List같은 컬렉션 객체를 이용하여 데이터 집합을 사용해야 하는 경우


이때 컬렉션 객체를 의존성 주입하면 되는데, 스프링에서는 이를 위해 컬렉션 매핑과 관련된 엘리먼트를 지원한다.


컬렉션 유형

 엘리먼트

 java.util.List, 배열

 <list>

 java.util.Set

 <set>

 java.util.Map

 <map>

 java.util.Propeties

 <props>




List 타입 매핑


배열 객체나 java.util.List 타입의 컬렉션 객체는 <list> 태그를 사용하여 설정한다.

먼저 List컬렉션을 멤버변수로 가지는 CollectiionBean 클래스를 다음과 같이 작성한다.




CollectionBean.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package polymorphism;
 
import java.util.List;
 
public class CollectionBean{
    private List<String> addressList;
 
    public void setAddressList(List<String> addressList){
        this.addressList = addressList;
    }
    public List<String> getAddressList(){
        return addressList;
    }
}
cs



작성된 CollectionBean 클래스를 스프링설정 파일에 다음과 같이 <bean> 등록한다.




applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <bean id="tv" class="com.springbook.ioc.injection.CollectionBean">
        <property name="addressList">
            <list>
                <value>서울시 강남구 역삼동</value>
                <value>서울시 성동구 행당동</value>
            </list>
        </property>
    </bean>    
</beans>
cs


위 설정은 두 개의 문자열 주소가 저장된 List객체를 CollectionBean 객체의 setAdressList()메소드를 호출할 때,

인자로 전달하여 addressList 멤버변수를 초기화하는 설정이다.



이제 클라이언트 프로그램으로 List 컬렉션이 정상적으로 의존성 주입 되었는지 확인해 본다.


<실행결과>

서울시 강남구 역삼동

서울시 성동구 성수동






Set 타입 매핑


중복 값을 허용하지 않는 집합 객체를 사용할때는 java.util.Set이라는 컬렉션을 사용한다.

컬렉션 객체는 <set>태그를 사용하여 설정할 수 있다.


Java File

1
2
3
4
5
6
7
8
9
10
package com.springbook.ioc.injection;
 
public class CollectionBean {
    private Set<String> addressList;
    
    public void setAddressList(Set<String> addressList){
        this.addressList - addressList;
    }
}
 
cs


Xml File

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <bean id="tv" class="com.springbook.ioc.injection.CollectionBean">
        <property name="addressList">
            <set value-type="java.lang.String">
                <value>서울시 강남구 역삼동</value>
                <value>서울시 성동구 성수동</value>
                <value>서울시 성동구 성수동</value>
            </set>
        </property>
    </bean>    
</beans>
cs


setAddressList() 메소드를 호출할때, 문자열 타입의 데이터 여러개를 저장한 set컬렉션을 인자로 전달하겠다는 설정


그런데 위 설정을 보면 "서울시 성동구 성수동"이라는 주소가 두번 등록된 것을 확인할 수 있다

그러나 Set 컬렉션은 같은 데이터를 중복해서 저장하기 않으므로 실제 실행해보면

"서울시 성동구 성수동"이라는 주소는 하나만 저장된다.






xml 설정 / java 설정 (장단점)


각 방식마다 장점과 단점이 있고, 개발자마다 선호하는 방식이 다르다. 


XML방식

⊙ 설정 정보를 변경할 때에는 XML만 변경하면 된다.
⊙ 많은 프레임워크/라이브러리가 XML 스키마를 이용한 설정의 편리함을 지원한다.

⊙ (IDE의 코드 자동 완성 기능이 빈약하면) XML 작성과정이 다소 번거롭다.

⊙ 코드를 실행해야 설정 정보 오류를 확인할 수 있다.


JAVA방식

⊙ 컴파일러의 도움을 받기 때문에, 오타 등의 설정 정보 오류를 미리 알 수있다.

⊙ 자바 코드이기 때문에 IDE가 제공하는 코드 자동 완성기능의 도움을 받을 수 있다.

⊙ 설정 정보를 변경하려면 자바 코드를 재컴파일해주어야 한다.

⊙ XML 스키마 기반의 설정을 지원하는 프레임워크/라이브러리 중 아직 자바 기반의 편리한 설정을 지원하지 않는 경우가 있다.


각각의 장점이 서로 다르기 때문에. 한 가지 방식만 고집해서 사용하기 보다는 상황에 따라 알맞게 방식을 조합해서 사용하곤 한다.

Comments