교재 실습/자바 웹 개발 워크북

141. 컬렉션 값 주입 (2)

Jint 2022. 10. 26. 22:34

2. Map과 Properties 값 주입

빈 프로퍼티 타입이 java.util.Map이나 java.util.Properties인 경우, 식별자(key)와 값(value)을 한 쌍으로 묶어서 저장한다. 이 타입의 프로퍼티 값은 어떻게 설정하는지 알아본다.

 

- 실습 패키지 생성

exam 패키지 아래에 test10 패키지를 생성한다. test09 패키지의 모든 파일을 복사해온다.

 

- 실습 클래스 변경

Map 객체와 Properties 객체를 실습할 수 있도록 기존 클래스에 프로퍼티를 추가한다. Tire 클래스는 타이어 규격을 표현하는 spec 프로퍼티의 타입을 java.lang.String에서 java.util.Properties로 변경한다. 또한, toString() 메서드도 그에 맞추어 변경한다. Car 클래스는 자동차의 선택 사항을 표현하는 options 프로퍼티를 추가한다.

exam.test10.Tire 클래스를 다음과 같이 편집한다.

package exam.test10;

import java.util.Date;
import java.util.Map.Entry;
import java.util.Properties;

public class Tire {
	
	String maker; //제조사
	Properties spec; //규격
	Date createdDate; //제조일
	
	public String getMaker() {
		return maker;
	}
	
	public void setMaker(String maker) {
		this.maker = maker;
	}
	
	public Properties getSpec() {
		return spec;
	}

	public void setSpec(Properties spec) {
		this.spec = spec;
	}

	public Date getCreatedDate() {
		return createdDate;
	}
	
	public void setCreatedDate(Date createdDate) {
		this.createdDate = createdDate;
	}
	
	@Override
	public String toString() {
		StringBuffer specInfo = new StringBuffer();
		if(spec != null) {
			for(Entry<Object,Object> entry : spec.entrySet()) {
				specInfo.append(entry.getKey() + ":" + entry.getValue() + ",");
			}
		}
		return "[Tire:" + maker + "," + specInfo.toString() + ((createdDate != null) ? ("," + createdDate.toString()) : "") + "]";
	}
	
}

이전 소스에서 인스턴스 변수 spec은 java.lang.String 타입이었다. 타이어 규격 정보를 좀 더 세분화해서 저장하기 위해 java.util.Properties 타입으로 변경하였다. 그에 따라 겟터/셋터와 toString() 메서드를 변경하였다.

for(Entry<Object,Object> entry : spec.entrySet()) {
    specInfo.append(entry.getKey() + ":" + entry.getValue() + ",");
}

Properties 객체의 entrySet() 메서드는 식별자와 값을 한 쌍으로 묶은 엔트리(entry)를 반환한다. Entry 객체에서 식별자를 꺼낼 때는 getKey() 메서드, 값을 꺼낼 때는 getValue() 메서드를 호출한다.

exam.test10.Car 클래스를 다음과 같이 편집한다.

package exam.test10;

import java.util.Map;

public class Car {
	
	String model; //모델명
	Engine engine; //엔진
	Tire[] tires; //타이어
	Map<String,Object> options; //선택사항
	
	public Car() {}
	
	public Car(String model, Engine engine) {
		this.model = model;
		this.engine = engine;
	}
	
	public String getModel() {
		return model;
	}
	
	public void setModel(String model) {
		this.model = model;
	}
	
	public Engine getEngine() {
		return engine;
	}
	
	public void setEngine(Engine engine) {
		this.engine = engine;
	}
	
	public Tire[] getTires() {
		return tires;
	}

	public void setTires(Tire[] tires) {
		this.tires = tires;
	}
	
	public Map<String, Object> getOptions() {
		return options;
	}

	public void setOptions(Map<String, Object> options) {
		this.options = options;
	}

	@Override
	public String toString() {
		StringBuffer carInfo = new StringBuffer();
		carInfo.append("[Car:" + model);
		carInfo.append("\n  " + engine.toString());
		if(tires != null) {
			for(Tire tire : tires) {
				carInfo.append("\n  " + tire.toString());
			}
		}
		carInfo.append("\n]");
		return carInfo.toString();
	}
	
}

이전 소스와 비교해 달라진 점은 자동차 옵션 정보를 관리할 java.util.Map 타입의 options 변수를 추가한 것이다. options 프로퍼티를 위한 겟터/셋터도 추가했다.

 

※ Map과 Properties의 용도

java.util.Map 타입의 클래스는 식별자(key)나 값(value)으로 문자열뿐만 아니라 다른 타입(클래스 또는 인터페이스)의 객체도 사용할 수 있다. java.util.Properties 클래스도 Map의 일종(Map 인터페이스를 구현하였음)이지만 주로 문자열로 된 식별자와 값을 다룰 때 사용한다.

 

- beans.xml 빈 설정 파일

exam/test10/beans.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:p="http://www.springframework.org/schema/p"
    xmlns:c="http://www.springframework.org/schema/c"
 	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="spareTire" class="exam.test10.Tire">
		<property name="maker" value="Hyundai"/>
		<property name="spec">
			<props>
				<prop key="width">205</prop>
				<prop key="ratio">65</prop>
				<prop key="rim.diameter">14</prop>
			</props>
		</property>
	</bean>

	<bean id="car1" class="exam.test10.Car">
		<constructor-arg value="Avante"/>
		<constructor-arg>
			<bean class="exam.test10.Engine" p:maker="Hyundai" p:cc="1495"/>
		</constructor-arg>
		<property name="options">
			<map>
				<entry>
					<key>
						<value>sunroof</value>
					</key>
					<value>yes</value>
				</entry>
				<entry key="airbag" value="dual"/>
				<entry key="sparetire">
					<ref bean="spareTire"/>
				</entry>
			</map>
		</property>
	</bean>

</beans>

 

<bean id="spareTire" class="exam.test10.Tire">
    <property name="maker" value="Hyundai"/>
    <property name="spec">
        <props>
            <prop key="width">205</prop>
            <prop key="ratio">65</prop>
            <prop key="rim.diameter">14</prop>
        </props>
    </property>
</bean>

 

▶ 자바 코드로 표현하면?
Tire spareTire = new Tire();
spareTire.setMaker("Hyundai");
Properties temp = new Properties();
temp.setProperty("width", "205");
temp.setProperty("ratio", "65");
temp.setProperty("rim.diameter", "14");
spareTire.setSpec(temp);

java.util.Properties 타입의 값을 설정할 때는 <props> 태그를 사용한다. Properties 객체에 저장할 항목은 <prop> 태그로 정의한다. <prop> 태그의 key 속성에는 문자열로 된 식별자가 들어가고, <prop> 태그와 </prop> 태그 사이에는 상숫값이 들어간다.

<bean id="car1" class="exam.test10.Car">
    <constructor-arg value="Avante"/>
    ...
</bean>

 

▶ 자바 코드로 표현하면?
Car car1 = new Car("Avante", ...);

car1 인스턴스를 생성하고, 생성자에 자동차 모델명 "Avante"와 엔진 객체를 새로 만들어 넘긴다.

<property name="options">
    <map>
        <entry>
            <key>
                <value>sunroof</value>
            </key>
            <value>yes</value>
        </entry>
        <entry key="airbag" value="dual"/>
        <entry key="sparetire">
            <ref bean="spareTire"/>
        </entry>
    </map>
</property>

 

▶ 자바 코드로 표현하면?
Map<Object,Object> tempMap = new HashMap<Object,Object>();
tempMap.put("sunroof", "yes");
tempMap.put("airbag", "dual");
tempMap.put("sparetire", spareTire);
car1.setOptions(tempMap);

options 프로퍼티는 java.util.Map 타입인데 이 타입의 값을 설정할 때는 <map> 태그를 사용한다.

<entry>
    <key>
        <value>sunroof</value>
    </key>
    <value>yes</value>
</entry>

맵에 들어갈 식별자와 값은 <entry> 태그로 정의한다. 엔트리의 식별자를 설정할 때는 <key> 태그를 사용한다. <key> 태그에 직접 식별자를 넣을 수는 없고 <value> 태그를 사용해 넣는다. 엔트리의 값으로 상숫값을 설정할 때는 <value> 태그를 사용한다.

<entry key="airbag" value="dual"/>

 

▶ 자바 코드로 표현하면?
tempMap.put("airbag", "dual");

<entyn> 태그도 key, value 속성을 사용하여 간략한 방식으로 식별자와 값을 설정할 수 있다.

<entry key="sparetire">
    <ref bean="spareTire"/>
</entry>

 

▶ 자바 코드로 표현하면?
tempMap.put("sparetire", spareTire);

엔트리의 값으로 <ref> 태그를 사용하여 다른 객체의 레퍼런스를 설정할 수 있다. 새로 객체를 생성하여 설정하려면 <bean> 태그를 사용하면 된다. 값을 설정하는 방법은 <property> 태그와 같다.

 

- 빈 컨테이너 테스트

Properties 타입과 Map 타입의 프로퍼티에 값이 잘 들어갔는지 확인해 본다. exam.test10.Test 클래스를 다음과 같이 편집한다.

package exam.test10;

import java.util.Map.Entry;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	
	public static void main(String[] args) {
		//IoC 컨테이너 준비하여 빈 생성
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("exam/test10/beans.xml");
		
		System.out.println("[Propoerties 타입]---------------------");
		Tire spareTire = (Tire)ctx.getBean("spareTire");
		for(Entry<Object,Object> entry : spareTire.getSpec().entrySet()) {
			System.out.println(entry.getKey() + ":" + entry.getValue());
		}
		
		System.out.println("[Map 타입]---------------------");
		Car car1 = (Car)ctx.getBean("car1");
		for(Entry<String,Object> entry : car1.getOptions().entrySet()) {
			System.out.println(entry.getKey() + ":" + entry.getValue());
		}
	}
	
}

 

Tire spareTire = (Tire)ctx.getBean("spareTire");
for(Entry<Object,Object> entry : spareTire.getSpec().entrySet()) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}

그림 1 (exam.test10.Test 클래스 실행 결과)

빈 컨테이너에서 'spareTire' 객체를 꺼낸다. spec 프로퍼티에 들어 있는 엔트리 객체의 식별자와 값을 출력한다.

Car car1 = (Car)ctx.getBean("car1");
for(Entry<String,Object> entry : car1.getOptions().entrySet()) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}

그림 2 (exam.test10.Test 클래스 실행 결과)

'car1'의 option 프로퍼티는 Map 타입이다. spec 프로퍼티처럼 entrySet() 메서드와 반복문을 사용하여 맵에 들어있는 값을 출력한다. options의 맵 타입이 java.util.Map<String,Object>으로 선언되었기 때문에, 이 맵으로부터 꺼내는 엔트리 타입도 Entry<String,Object>가 된다.

실행 결과를 보니 빈 설정 파일에 정의된 대로 Properties 객체와 Map 객체에 설정된 값이 정확히 출력됨을 알 수 있다.

 

참고도서 : https://freelec.co.kr/book/1674/

 

[열혈강의] 자바 웹 개발 워크북

[열혈강의] 자바 웹 개발 워크북

freelec.co.kr