스프링프레임워크 제어의역행(Spring Ioc) 실습(XML 기반, 인터페이스, 세터주입)
“오라클자바몰”이라는 회사를 만들어서 내가 잘아는 분야인 차를 팔기로 정했다. 차량의 구매는 현대자동차와 하고, 현대자동차에 돈을 지불하고 차를 받기로 했다.
STS
New >> Project >> Spring Legacy Project
Project Name : carmaker
Simple Projects : Simple Spring Maven
프로젝트 생성 후
- 프로젝트 루트아래의 pom.xml 파일을 오픈 후 스프링 버전을 4.2.0으로 변경
- src/main/java아래에서 edu.biz.ioc 패키지 생성
<!-- Spring -->
<spring-framework.version>4.2.0.RELEASE</spring-framework.version>
[Money.java]
package edu.biz.ioc;
public class Money {
private int amount;
public Money() {}
public Money(int amt) {
this.amount = amt;
}
public int getAmount() {
return this.amount;
}
public void setAmount(int amt) {
this.amount = amt;
}
}
[Car.java]
package edu.biz.ioc;
public class Car{
private String name;
public Car(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
[HyundaiMaker.java]
package edu.biz.ioc;
public class HyundaiMaker {
public HyundaiMaker () { } //생성자
public Car sell(Money money) {
System.out.println("I sold a car.");
Car car = new Car(“Sonata”);
return car;
}
}
[OrderManager.java]
package edu.biz.ioc;
public class OrderManager {
private HyundaiMaker maker;
public OrderManager () {
this.maker = new HyundaiMaker();
}
public void order() {
Car car = this.maker.sell(new Money(1000));
}
}
[src/main/rtesources/ioc.xml]
Project에서 마우스 우측버튼 -> new -> other -> Spring -> Spring Bean Configuration File
<?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-4.2.xsd">
<bean id="orderManager" class="edu.biz.ioc.OrderManager"/>
</beans>
[OrderManagerApp.java]
package edu.biz.ioc;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.*;
import org.springframework.core.io.*;
public class OrderManagerApp {
public static void main(String[] args) {
BeanFactory factory =
new XmlBeanFactory(new FileSystemResource(“src/main/resources/ioc.xml"));
OrderManager manager = (OrderManager)
factory.getBean("orderManager");
manager.order();
}
}
위 예제에서
OrderManager와 HyundaiMaker는 서로 떼어 놓을 방법이 없다. 이 두 클래스는 정적으로 결합(coupling)되어 있다
하나의 버그를 고치면 그 결과 다른 새로운 버그가 생성되는 ‘두더지잡기(whack-a-mole)’ 버그를 만든다.
다른 한편으로 완벽하게 분리된 코드는 아무일 도 못한다.
자바에서 결합도를 떨어뜨리는 일반적인 방법은 구현을 인터페이스 뒤로 숨기는 인터페이스 기반 프로그래밍을 하는 것이다. 이를 통해 클라이언트 클래스에 영향을 주지 않고도 쉽게 구현 클래스가 교체될 수 있도록 할 수 있다.
[CarMaker.java]
package edu.biz.ioc;
public interface CarMaker {
/** 돈을 받고 차를 판다. */
public Car sell(Money money) ;
}
[HyundaiMaker.java를 인터페이스를 구현하도록 수정]
package edu.biz.ioc;
public class HyundaiMaker implements CarMaker{
public HyundaiMaker() { }
public Car sell(Money money) {
System.out.println("I sold a car.");
Car car = new Car("Sonata");
return car;
}
}
[OrderManager.java 수정]
package edu.biz.ioc;
public class OrderManager {
//만약 인터페이스를 사용하지 않는다면 HyundaiMaker 다른 메이커로 교체된다
//면 아래의 private CarMaker maker를 포함하여 소스코드가 수정되어야 하지만
//인터페이스를 사용한다면 new HyundaiMaker () 부분만 수정하면 된다.
private CarMaker maker; //인터페이스 타입
public OrderManager() {
//인터페이스기반이므로 추후 DaewooMaker()로 수정 용이.
this.maker = new HyundaiMaker();}
public void order() {
Car car = this.maker.sell(new Money(1000));
}
}
수정된 코드에 대한 검토
인터페이스 기반이므로 new HyundaiMaker 부분은 쉽게 다른 메이커로 변경 가능하지만 여전히 HyundaiMaker를 new 하고 있음. 강한 의존관계
자주 변경되는 구상클래스(Concrete class)에 의존하지 말라.
어떤 클래스를 상속받아야 한다면 , 기반 클래스를 추상 클래스 또는 인터페이스로 만들어서 구현하라.
어떤 클래스의 참조를 가져야 한다면 참조 대상이 되는 클래스를 추상 클래스, 인터페이스로 정의하라.
인터페이스를 만들어서 이 인터페이스에 의존하라. DIP(Dependency Inversion Principle)
[DaewooMaker.java]
package edu.biz.ioc;
public class DaewooMaker implements CarMaker {
public DaewooMaker () { }
public Car sell(Money money) {
System.out.println("I sold a car.");
Car car = new Car(“Tosca”);
return car;
}
}
[OrderManager.java 수정]
package edu.biz.ioc;
public class OrderManager {
private CarMaker maker;
public OrderManager(String name) {
this.maker = new DaewooMaker();
}
public void order() {
Car car = this.maker.sell(new Money(1000));
}
}
실행결과를 확인하여 DaewooMaker로 바뀐것을 확인하자.
인스턴스를 생성하는 방법의 문제를 해결하기 위해 스프링 프레임워크의 세터주입을 이용하자.
[OrderManager.java 수정]
public class OrderManager {
private String name;
private CarMaker maker;
public OrderManager() { }
public void order() {
Car car = this.maker.sell(new Money(1000));
}
//setter 메소드를 통해 CarMaker 타입 객체를 주입 받는다.
public void setMaker(CarMaker maker) {
this.maker = maker;
}
}
[ioc.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-4.2.xsd">
<beans>
<bean id="hyundaiMaker" class=" edu.biz.ioc.HyundaiMaker"/>
<bean id=“daewooMaker" class=" edu.biz.ioc.DaewooMaker"/>
<bean id="orderManager"
class="edu.biz.ioc.OrderManager">
<!-- 세터 주입을 의미 -->
<property name="maker">
<!-- 이부분을 수정하면 소스수정 없이 maker 교체 가능 -->
<ref bean="hyundaiMaker"/>
</property>
</bean>
</beans>
OrderManagerApp 실행 후 결과 확인하자.
#스프링IoC, #제어의역행, #마틴파울러, #스프링교육, #스프링강좌, #IoC, #SpringIoC