728x90


저번 시간 코드가 기억이 나는가? 만약 나지 않는다면 저번시간 코드를 참조하라.

저번 시간 코드에서 이어서 설명을 할것이다.


저번시간에 bean과 xml을 사용해서 pickChamp를 호출했었던 것이 기억이나는가?

이 때 pickChamp를 각각 riven과 evelynn에게 줬을때 각각을 호출했었다.

이때 여러분은 처음 값을 어떻게 초기화 시켜주었는가?

저번 코드를 조금 변경시켜보자. 바로 riven과 evelynn에 생성자를 추가시켜보자.


##class Riven

package champ;

public class Riven implements Champ {
int ad;
int ap;
int speed;
String skill1;
String skill2;
String skill3;
String skill4;

{
ad = 60;
ap = 0;
speed = 340;
skill1 = "부서진 날개";
skill2 = "기 폭팔";
skill3 = "용맹";
skill4 = "추방자의 검";
}

public Riven() {
System.out.println("Riven.Riven()");
}

@Override
public void attack() {
System.out.println("Riven.attack : " + ad);
}

@Override
public void move() {
System.out.println("Riven.move : " + speed);
}

@Override
public void skill1() {
System.out.println("Riven.skill1 : " + skill1);
}

@Override
public void skill2() {
System.out.println("Riven.skill2 : " + skill2);
}

@Override
public void skill3() {
System.out.println("Riven.skill3 : " + skill3);
}

@Override
public void skill4() {
System.out.println("Riven.skill4 : " + skill4);
}
}


##class Evelynn

package champ;

public class Evelynn implements Champ {
int ad;
int ap;
int speed;
String skill1;
String skill2;
String skill3;
String skill4;

{
ad = 50;
ap = 30;
speed = 335;
skill1 = "증오의 가시";
skill2 = "황홀한 저주";
skill3 = "채찍유린";
skill4 = "최후의 포옹";
}

public Evelynn() {
System.out.println("Evelynn.Evelynn()");
}

@Override
public void attack() {
System.out.println("Evelynn.attack : " + ad);
}

@Override
public void move() {
System.out.println("Evelynn.move : " + speed);
}

@Override
public void skill1() {
System.out.println("Evelynn.skill1 : " + skill1);
}

@Override
public void skill2() {
System.out.println("Evelynn.skill2 : " + skill2);
}

@Override
public void skill3() {
System.out.println("Evelynn.skill3 : " + skill3);
}

@Override
public void skill4() {
System.out.println("Evelynn.skill4 : " + skill4);
}
}


다시 호출해보자. 그러면 생성자가 호출되는걸 확인할 수 있다.

즉 컨테이너를 사용해서 호출 했을 경우 생성자를 바탕으로 초기화를 한다. 이 방식은 뭐 일반적으로 똑같다.

디폴트 생성자를 호출하거나 디폴트 생성자가 소멸하였다면, 파라메터가 없는 생성자를 호출하게 된다.


그런데 이런 방식은 그냥 사용해도될까?

뭐 사실 생성자는 별로 중요치 않은데 필드를 초기화시키는 방식을 이야기하는 것이다.

저번 IoC에서 했던 이야기인데 자바코드를 사용하면 객체끼리의 결합이 강해진다고 하였다.

그 결합을 약하게 하기 위해서 사용한 것이 IoC이다.

그런데 자바코드를 최대한 줄이게 하기 위해서 IoC를 썼는데 결국 생성자 호출에서 또 자바코드를 사용하게 된다.

생성자를 쓰던 초기화 블록을 쓰던 결국 자바코드를 사용해서 특정 클래스의 field들을 초기화 시켜주는 것이다.

그럼 이렇게 초기화 역시 xml설정파일을 쓰게되면 더 결합도가 줄지 않을까?

자바 클래스는 결국 정말로 비즈니스 로직만 담겨 있는 것이다. 그 외의 생성자 호출이나 필드 값 설정들은 설정파일로 하게 하는 것이 바람직하다.

이렇게 초기화를 xml 설정파일을 이용해서 주입(Injection)해주는 것을 의존성(Dependency)을 주입한다고 하여 Dependency Injection, 즉 DI라 부른다.


설명이 길었다. DI가 왜 필요한지를 설명하면 위와 같다.

결론은 DI가 하는 역활은 초기화를 xml로 해주는 방법론이라고 할 수 있겠다.

이제 DI를 하는 방법에 대해서 알아보자.


DI방식


1.생성자(Constructor) 인젝션

2.설정자(Setter) 인젝션


생성자 인젝션


아래 같은 상황을 가정해보자. 메이플 스토리를 떠올려보자. 모르면... 그냥 하자.

Player클래스를 인터페이스로 Warrior클래스와 Mage클래스가 상속된다.

그리고 Warrior와 Mage는 무기(Weapon)클래스를 하나 가지고있다.

또한 Weapon클래스는 각각 Wand와 Sword에 상속된다.

이를 코드화하면 아래와 같다.


#interface Player

package maple;

public interface Player {
void attack();

void skill();
}

#class Warrior
package maple;

public class Warrior implements Player {
private int hp;
private int mp;
private Weapon weapon;

public Warrior() {

}

public Warrior(int hp, int mp, Weapon weapon) {
this.hp = hp;
this.mp = mp;
this.weapon = weapon;
}

public int getHp() {
return hp;
}

public void setHp(int hp) {
this.hp = hp;
}

public int getMp() {
return mp;
}

public void setMp(int mp) {
this.mp = mp;
}

public Weapon getWeapon() {
return weapon;
}

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

@Override
public void attack() {
System.out.println("전사의 공격");
}

@Override
public void skill() {
System.out.println("전사의 스킬");
}

@Override
public String toString() {
return "Warrior [hp=" + hp + ", mp=" + mp + ", weapon=" + weapon + "]";
}
}

#class Mage
package maple;

public class Mage implements Player {
private int hp;
private int mp;
private Weapon weapon;

public Mage() {

}

public Mage(int hp, int mp, Weapon weapon) {
this.hp = hp;
this.mp = mp;
this.weapon = weapon;
}

public int getHp() {
return hp;
}

public void setHp(int hp) {
this.hp = hp;
}

public int getMp() {
return mp;
}

public void setMp(int mp) {
this.mp = mp;
}

public Weapon getWeapon() {
return weapon;
}

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

@Override
public void attack() {
System.out.println("마법사의 공격");
}

@Override
public void skill() {
System.out.println("마법사의 스킬");
}

@Override
public String toString() {
return "Mage [hp=" + hp + ", mp=" + mp + ", weapon=" + weapon + "]";
}
}

#interface Weapon
package maple;

public interface Weapon {
void sell();

void showStat();
}

#class Wand
package maple;

public class Wand implements Weapon {
private String name;

public Wand(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public void sell() {
System.out.println(name + "은 Wand인데 팔아버립니다.");
}

@Override
public void showStat() {
System.out.println("주로 마법사들이 사용하는 지팡이 입니다.");
}

@Override
public String toString() {
return "Wand [name=" + name + "]";
}
}

#class Sword
package maple;

public class Sword implements Weapon {
private String name;

public Sword(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public void sell() {
System.out.println(name + "은 Sword인데 팔아버립니다.");
}

@Override
public void showStat() {
System.out.println("주로 전사들이 사용하는 검입니다.");
}

@Override
public String toString() {
return "Sword [name=" + name + "]";
}
}

이제 생성자 인젝션을 사용해서 위 소스들을 등록시켜보자.

먼저 일반적으로 Player를 상속받은 클래스들은 Weapon에 의존성을 가진다.

따라서 Weapon에 injection해보자


<?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="wand" class="maple.Wand">
<constructor-arg value="아케인셰이드 완드"></constructor-arg>
</bean>
<bean id="sword" class="maple.Sword">
<constructor-arg value="아케인셰이드 세이버"></constructor-arg>
</bean>
</beans>


먼저 생성자의 파라메터 갯수에 맞게 파라메터를 집어 넣어야한다.

Wand의 경우 생성자가 파라메터 한개를 받으므로 반드시 파라메터를 한개 넣는다.

만약 생성자가 여러개일 경우 생성자의 파라메터 갯수와 타입으로 선택할 수있다.

생성자의 파라메터는 constructor-arg라는 프로퍼티로 선택할 수 있다.

문자열이나 프리미티브 타입은 value라는 값으로 넣는다.


여기서 이제 Player를 인젝션해보자.


<?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="warrior" class="maple.Warrior">
<constructor-arg value="500"></constructor-arg>
<constructor-arg value="300"></constructor-arg>
<constructor-arg ref="sword"></constructor-arg>
</bean>
<bean id="mage" class="maple.Mage">
<constructor-arg value="300"></constructor-arg>
<constructor-arg value="500"></constructor-arg>
<constructor-arg ref="wand"></constructor-arg>
</bean>

<bean id="wand" class="maple.Wand">
<constructor-arg value="아케인셰이드 완드"></constructor-arg>
</bean>
<bean id="sword" class="maple.Sword">
<constructor-arg value="아케인셰이드 세이버"></constructor-arg>
</bean>
</beans>


Warrior나 Mage에 인젝션을 하려면 생성자를 선택해야한다.

둘의 생성자는 파라메터가 0개인 생성자와 파라메터가 3개인 생성자가 존재한다.

파라메터가 0개라면 어짜피 안에 아무것도 적을 필요가 없고

파라메터가 3개인 생성자를 사용한다면 그 순서를 지켜야한다.

파라메터의 순서대로 만들면 되는데 Warrior의경우 int,int,Weapon순으로 되어있다.

따라서 int,int,Weapon순으로 넣어주면 되는데 여기서 문제가 일반적인 value로 넣을 수 없다는 것이다.

여기서 Weapon의 경우 레퍼런스이므로 ref로 넣어주면 된다.

여기서 사용하는 ref의 이름은 bean으로 등록되어 있어야한다.

warrior bean의 경우 sword bean이 등록되어 있어야하고 mage bean은 wand bean이 등록 되어 있어야한다는 것이다.


이렇게 되면 이제 준비는 끝났다. 호출 하는 방식은 저번과 같다고 할 수 있다.


package maple;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class Main {
public static void main(String[] args) {
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
Player warrior = (Player) factory.getBean("warrior");
Player mage = (Player) factory.getBean("mage");
System.out.println(warrior);
System.out.println(mage);
}
}


문제 없이 호출되는 것을 확인할 수 있다.

여기서 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="warrior" class="maple.Warrior">
<constructor-arg index="1" value="500"></constructor-arg>
<constructor-arg index="0" value="300"></constructor-arg>
<constructor-arg index="2" ref="sword"></constructor-arg>
</bean>
<bean id="mage" class="maple.Mage">
<constructor-arg value="300"></constructor-arg>
<constructor-arg value="500"></constructor-arg>
<constructor-arg ref="wand"></constructor-arg>
</bean>

<bean id="wand" class="maple.Wand">
<constructor-arg value="아케인셰이드 완드"></constructor-arg>
</bean>
<bean id="sword" class="maple.Sword">
<constructor-arg value="아케인셰이드 세이버"></constructor-arg>
</bean>
</beans>


여기서 constructor-arg에 index라는 속성이 보인다. 이는 명시적으로 몇번째 파라메터에 넣는지 알려주는 것이다.

사실 실제로 크게 사용할 필요는 없다. 그냥 명시적이게 적어줄 뿐이다.


<?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="warrior" class="maple.Warrior">
<constructor-arg type="int" value="500"></constructor-arg>
<constructor-arg type="int" value="300"></constructor-arg>
<constructor-arg type="maple.Weapon" ref="sword"></constructor-arg>
</bean>
<bean id="mage" class="maple.Mage">
<constructor-arg value="300"></constructor-arg>
<constructor-arg value="500"></constructor-arg>
<constructor-arg ref="wand"></constructor-arg>
</bean>

<bean id="wand" class="maple.Wand">
<constructor-arg value="아케인셰이드 완드"></constructor-arg>
</bean>
<bean id="sword" class="maple.Sword">
<constructor-arg value="아케인셰이드 세이버"></constructor-arg>
</bean>
</beans>


또한 속성으로 type도 있다. 여기에서 타입을 명시적이게 선언하는 방식으로 구별하는 것 역시 가능하다.

+ Recent posts