본문 바로가기
교재 실습/자바 웹 개발 워크북

138. 의존 객체 주입 (1)

by Jint 2022. 10. 23.

어떤 객체가 작업을 수행하기 위해 다른 객체를 지속적으로 사용한다면 그 사용되는 객체를 의존 객체(depencencies)라 부른다. 보통 지속적으로 사용할 객체는 프로퍼티에 보관한다. 이번 절에서는 바로 이 프로퍼티 값으로 의존 객체를 주입하는 방법을 배운다.

 

1. 의존 객체 설정하기

지금까지는 프로퍼티 값으로 문자열 또는 숫자를 설정하였다. 이번에는 프로퍼티 값으로 객체를 지정하는 방법을 알아본다. 의존 객체 사용과 설정에 대한 이해를 돕기 위해 특별한 구조의 클래스를 준비할 것이다. 다음은 실습 클래스의 관계도이다(그림 1).

그림 1 (의존 객체와 포함 관계)

Car 객체는 Engine 객체와 Tire 객체를 필요로 한다. 특히 Tire 객체는 여러 개를 갖는다.

 

- 실습 패키지 생성

exam 패키지 아래에 test07 패키지를 생성한다. test06 패키지에서 beans.xml과 Test.java 파일을 복사해온다.

 

- 실습 클래스 생성

exam.test07 패키지에 Engine 클래스를 생성하고 다음과 같이 편집한다. Engine 클래스는 엔진 제조사와 배기량 정보를 다루는 클래스이다.

package exam.test07;

public class Engine {
	
	String maker; //제조사
	int cc; //배기량
	
	public Engine() {}
	
	public Engine(String maker) {
		this.maker = maker;
	}
	
	public String getMaker() {
		return maker;
	}
	
	public void setMaker(String maker) {
		this.maker = maker;
	}
	
	public int getCc() {
		return cc;
	}
	
	public void setCc(int cc) {
		this.cc = cc;
	}
	
	@Override
	public String toString() {
	  return "[Engine:" + maker + "," + cc + "]";
	}
	
}

exam.test07 패키지에 Tire 클래스를 생성하고 다음과 같이 편집한다. Tire 클래스는 타이어 제조사와 규격, 제조일 정보를 다루는 클래스이다.

package exam.test07;

import java.util.Date;

public class Tire {
	
	String maker; //제조사
	String spec; //규격
	Date createdDate; //제조일
	
	public String getMaker() {
		return maker;
	}
	
	public void setMaker(String maker) {
		this.maker = maker;
	}
	
	public String getSpec() {
		return spec;
	}
	
	public void setSpec(String spec) {
		this.spec = spec;
	}
	
	public Date getCreatedDate() {
		return createdDate;
	}
	
	public void setCreatedDate(Date createdDate) {
		this.createdDate = createdDate;
	}
	
	@Override
	public String toString() {
		return "[Tire:" + maker + "," + spec + ((createdDate != null) ? ("," + createdDate.toString()) : "") + "]";
	}
	
}

 

@Override
public String toString() {
    return "[Tire:" + maker + "," + spec + ((createdDate != null) ? ("," + createdDate.toString()) : "") + "]";
}

Tire 클래스는 toString() 메서드를 재정의(overriding)하였는데, 객체의 내용을 간단한 문자열로 출력하는 일을 한다. 단 제조일(createdDate)은 null이 아닐 때만 출력하도록 처리하였다.

exam.test07 패키지에 Car 클래스를 생성하고 다음과 같이 편집한다. Car 클래스는 자동차 모델명과 엔진, 타이어 정보를 다루는 클래스이다.

package exam.test07;

public class Car {
	
	String model; //모델명
	Engine engine; //엔진
	Tire[] tires; //타이어
	
	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;
	}

	@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();
	}
	
}

 

- beans.xml 빈 설정 파일

exam/test07/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="engine1" class="exam.test07.Engine" c:maker="Hyundai" p:cc="1998"/>
	
	<bean id="car1" class="exam.test07.Car">
		<property name="model"><value>Avante</value></property>
		<property name="engine"><ref bean="engine1"/></property>
	</bean>
	
	<bean id="car2" class="exam.test07.Car">
		<property name="model" value="Sonata"/>
		<property name="engine" ref="engine1"/>
	</bean>
	
	<bean id="car3" class="exam.test07.Car" p:model="Grandeur" p:engine-ref="engine1"/>
	
	<bean id="car4" class="exam.test07.Car" c:model="Equus" c:engine-ref="engine1"/>

</beans>

 

<bean id="engine1" class="exam.test07.Engine" c:maker="Hyundai" p:cc="1998"/>
▶ 자바 코드로 표현하면?
Engine engine1 = new Engine("Hyundai");
engine1.setCc(1998);

첫 번째로 Engine 빈을 설정한다. 생성자의 매개변수 값과 빈의 프로퍼티 값을 설정하기 위해 'c' 네임스페이스와 'p' 네임스페이스의 속성을 선언한였다. c:maker 속성은 maker라는 이름의 매개변수가 있는 생성자를 호출하라는 의미이다. p:cc 속성은 빈에 대해 'cc' 프로퍼티 값을 1998로 설정하라는 뜻이다.

<bean id="car1" class="exam.test07.Car">
    <property name="model"><value>Avante</value></property>
    <property name="engine"><ref bean="engine1"/></property>
</bean>
▶ 자바 코드로 표현하면?
Car car1 = new Car();
car1.setModel("Avante");
car1.setEngine(engine1);

Car 빈을 생성한다. model 프로퍼티 값은 이전 실습에서 배운 대로 <value> 태그를 이용하여 설정한다. engine 프로퍼티 값은 Engine의 레퍼런스이어야 한다. 이를 위해 <ref> 태그를 사용하였다. bean 속성에 빈의 레퍼런스를 설정하면 된다. 예제 코드처럼 빈의 아이디 또는 이름, 별명을 지정한다. 이제 'car1'에는 'engine1'이 장착되었다.

<bean id="car2" class="exam.test07.Car">
    <property name="model" value="Sonata"/>
    <property name="engine" ref="engine1"/>
</bean>
▶ 자바 코드로 표현하면?
Car car2 = new Car();
car2.setModel("Sonata");
car2.setEngine(engine1);

빈의 프로퍼티 값을 지정할 때 태그 대신 속성을 사용하여 설정할 수 있다. 문자열이나 자바 기본 타입(byte, short, int, long, float, double, Boolean, char)에 해당하는 값은 value 속성을 사용하여 설정한다. 빈의 레퍼런스를 설정할 때는 ref 속성을 사용한다. 'car2' 또한 'car1'과 마찬가지로 'engine1'을 장착하였다. 즉 'car1'과 'car2'는 엔진을 공유하고 있다.

<bean id="car3" class="exam.test07.Car" p:model="Grandeur" p:engine-ref="engine1"/>
▶ 자바 코드로 표현하면?
Car car3 = new Car();
car3.setModel("Grandeur");
car3.setEngine(engine1);

'p' 네임스페이스의 속성을 사용하여 빈의 프로퍼티 값을 설정할 수 있다. 문자열이나 자바 기본 타입의 값은 'p:프로퍼티 이름' 형식으로 지정하고, 빈의 레퍼런스 값은 'p:프로퍼티 이름-ref' 형식으로 지정한다. 프로퍼티 이름 다음에 오는 '-ref'는 해당 프로퍼티의 값이 레퍼런스라는 것을 선언한다. 이제 'car3'도 'engine1'을 장착하였다.

<bean id="car4" class="exam.test07.Car" c:model="Equus" c:engine-ref="engine1"/>
▶ 자바 코드로 표현하면?
Car car4 = new Car("Equus", engine1);

생성자 매개변수도 프로퍼티와 같은 방식으로 설정한다. 'c' 네임스페이스 속성을 사용하여 생성자 매개변수 값을 지정한다. 매개변수 이름 뒤에 '-ref'가 붙으면 빈의 레퍼런스 주소를 설정하겠다는 뜻이다. 마지막 빈 'car4'도 'engine1'을 장착하였다. 앞의 빈 설정에서는 'car1'에서 'car4'까지 모든 자동차가 하나의 엔진을 공유하는 상황이다. 이를 확인하기 위해 테스트 해본다.

 

- 빈 컨테이너 테스트

빈 컨테이너의 동작을 테스트할 exam.test07.Test 클래스를 다음과 같이 편집한다.

package exam.test07;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	
	public static void main(String[] args) {
		//IoC 컨테이너 준비하여 빈 생성
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("exam/test07/beans.xml");
		
		Car car1 = (Car)ctx.getBean("car1");
		System.out.println(car1);
		
		Engine engine = (Engine)ctx.getBean("engine1");
		engine.cc = 3000;
		System.out.println(car1);
		
		Car car2 = (Car)ctx.getBean("car2");
		System.out.println(car2);
		
		Car car3 = (Car)ctx.getBean("car3");
		System.out.println(car3);
		
		Car car4 = (Car)ctx.getBean("car4");
		System.out.println(car4);
	}
	
}

 

Car car1 = (Car)ctx.getBean("car1");
System.out.println(car1);

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

빈 컨테이너에서 'car1'을 꺼내어 출력해 보면 위의 결과가 출력된다. 'engine1' 프로퍼티 값이 제대로 설정된 것을 알 수 있다.

Engine engine = (Engine)ctx.getBean("engine1");
engine.cc = 3000;
System.out.println(car1);

그림 3 (exam.test07.Test 클래스 실행 결과)

이번에는 'engine1'을 꺼내어 배기량(cc)을 변경한다. 그리고 다시 'car1'을 출력한다. 위의 실행 결과를 보면 엔진의 배기량이 3000으로 변경된 것을 확인할 수 있다.

'engine1' 객체는 'car1'뿐만 아니라 'car2', 'car3', 'car4'도 공유하기 때문에, 나머지 객체의 출력 결과를 확인해 보면 엔진의 배기량이 3000으로 변경된 것을 확인할 수 있다. 다음은 나머지 차의 출력 결과이다(그림 4).

그림 4 (exam.test07.Test 클래스 실행 결과)

자동차끼리 엔진을 공유하지 않고 각각 개별 엔진을 갖게끔 설정하는 방법은 없을까? 다음 실습에 그 방법이 나와 있다.

 

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

 

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

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

freelec.co.kr

댓글