Spring IcC
예제
이번에는 Spring에서 가장 강력하고도 중요한 기능 중 하나인 Ioc
혹은 DI 라고 불리우는 기능에 대해 간단한 예제를 통해 알아 보도록 한다.
오라클자바커뮤니티에서 설립한 오엔제이프로그래밍 실무교육센터
(오라클SQL, 튜닝, 힌트,자바프레임워크, 안드로이드, 아이폰, 닷넷 실무전문 강의)
1. IoC / DI 란 무엇인가?
IoC란 Inversion Of Controll 제어의 역행, 역제어 등으로 해석할 수 있다.
이는 기존 프로그래밍에서 객체의 생성, 제어, 소멸 등의 객체의 라이프 사이클을 개발자가 관리 하던 것을 컨테이너에게 그 제어권을 맡기는 프로그래밍 기법으로 Spring에서는 WAS가 컨테이너 역할을 한다.
즉, 필요에 따라 new 연산자를 이용하던 기존의 라이프 사이클 관리와 달리, WAS가 실행시 객체를 생성하고, 관리 하는 것이 스프링에서의 IoC이다.
* 2004년, 마틴 파울러는 제어의 어떤 측면이 역행되는 것인지에 대한 의문을 제기했고, 그는 의존하는 객체 를 역행적으로 취득하는 것이라는 결론을 내렸다. 그는 그와 같은 정의에 기초하여 제어의 역행이라는 용어
에 좀더 참신한 '의존성 주입(Depend-ency Injection)' 이라는 이름을 지워줬다. *
(발취 Spring inAction)
2. 의존성 주입에 의한 새로운 HelloWorld!
의존성 주입은 크게 setter주입과 생성자 주입으로 나뉜다. 먼저 setter주입에 대해 알아보도록 한다. 이번 예제는 이 전 예제에서 약간의 소스 수정을 통해 해보도록 하겠다. 먼저 다음과 같은 interface를 생성한다.
HelloBean.java
package spring.ex1;:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
public interface HelloBean {
public String getGreeting();
}
다음은 이 interface를 구현하는 Class를 생성한다.
HelloBeanImpl.java
package spring.ex1;
public String getGreeting();
}
다음은 이 interface를 구현하는 Class를 생성한다.
HelloBeanImpl.java
package spring.ex1;
public class HelloBeanImpl implements HelloBean {
public String getGreeting(){
return "DI Test SUCCESS!!";
}
}
Controller Class는 이전 예제에서 사용한 HelloController Class를 변경한다.
HelloController.java
package spring.ex1;
public String getGreeting(){
return "DI Test SUCCESS!!";
}
}
Controller Class는 이전 예제에서 사용한 HelloController Class를 변경한다.
HelloController.java
package spring.ex1;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import org.springframework.web.servlet.mvc.AbstractController;
public class HelloController extends
AbstractController{
private HelloBean helloBean;
private HelloBean helloBean;
public void setHelloBean(HelloBean helloBean) {
this.helloBean = helloBean;
}
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception{
ModelAndView mav = new ModelAndView();
mav.setViewName("hello");
mav.addObject("greeting",helloBean.getGreeting());
return mav;
}
}
자바 클래스의 코딩이 끝났으면 dispatcher-servlet.xml를 수정한다.
dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="helloBean" class="spring.ex1.HelloBeanImpl" />
<bean name="/hello.htm" class="spring.ex1.HelloController">
<property name="helloBean" ref="helloBean" />
</bean>
</beans>
수정이 끝났으면 다시 http://localhost:9090/SpringEx/hello.htm 로 접근하여 결과를 확인한다.정상적으로 입력 되었다면 다음과 같은 메세지가 뜬다.
this.helloBean = helloBean;
}
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception{
ModelAndView mav = new ModelAndView();
mav.setViewName("hello");
mav.addObject("greeting",helloBean.getGreeting());
return mav;
}
}
자바 클래스의 코딩이 끝났으면 dispatcher-servlet.xml를 수정한다.
dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="helloBean" class="spring.ex1.HelloBeanImpl" />
<bean name="/hello.htm" class="spring.ex1.HelloController">
<property name="helloBean" ref="helloBean" />
</bean>
</beans>
수정이 끝났으면 다시 http://localhost:9090/SpringEx/hello.htm 로 접근하여 결과를 확인한다.정상적으로 입력 되었다면 다음과 같은 메세지가 뜬다.
:namespace prefix = v ns = "urn:schemas-microsoft-com:vml"
/>
자, 이제 어떤 방식으로 의존성 주입이 되었는지 살펴 보도록 한다.먼저 HelloController 에서 변경된 부분을 살펴본다.
private HelloBean helloBean;
public void setHelloBean(HelloBean helloBean) {
this.helloBean = helloBean;
}
위와 같이 interface인 HelloBean을 'helloBean' 이라는 레퍼런스로 설정을 하고 setter 메서드를 통해 외부에서의 접근을 메서드로 받도록 하였다.
mav.addObject("greeting",helloBean.getGreeting());
mav.addObject에 helloBean.getGreeting() 메서드를 셋팅 하였다. helloBean은 interface이고 그 interface를 사용하려면 그를 구현한 HelloBeanImpl Class를 사용하여야 할 것 인대 어떻게 interface 를 사용하였는데 HelloIBeanImpl Class의 getGreeting() 메서드의 값을 출력할 수 있었을까?
다음 xml설정을 보도록 한다.
<bean id="helloBean" class="spring.ex1.HelloBeanImpl" />
<bean name="/hello.htm" class="spring.ex1.HelloController">
<property name="helloBean" ref="helloBean" />
</bean>
위와 같이 helloBean이라는 ID 값으로 HelloBeanImpl Class를 bean으로 선언하였다. 여기서 중요한 점은 HelloController에서 새로 추가된 property 이다.
이는 name에서 HelloController bean에서 존재하는 helloBean이라는 레퍼런스 값을 가진 객체를 ref 속성을 통해 xml에 선언된 ID : helloBean의 값을 가지는 객체를 setter 메서드를 통해 주입 시켜준 다는 것이다.
setHelloBean을 통해 HelloBeanImpl 은 helloBean 레퍼런스에 대입되고, HelloBeanImpl 은 HelloBean interface를 구현 하였기 때문에 HelloBean의 getGreeting()메서드를 오버라이딩 하게 된다.
그렇게 해서 HelloBean은 새로운 값을 주입 받게 되고, 그 결과를 JSP페이지로 리턴할 수 있게 된다.
그럼 이러한 방법으로 얻을 수 있는 이득은 무엇인가?
1. 객체의 라이프 사이클을 사용자가 아닌 서버가 관리 하기에 좀더 효율적인 관리를 할 수 있다.
2. 인터페이스에 의한 느슨한 결합에 의해 변덕 스러운 클라이언트에 요구 사항에 빠르게 대처 할 수 있다.
1번의 대해서는 위에서 설명 하였고, 2번 장점에 관한 예를 간단히 보도록 한다.
this.helloBean = helloBean;
}
위와 같이 interface인 HelloBean을 'helloBean' 이라는 레퍼런스로 설정을 하고 setter 메서드를 통해 외부에서의 접근을 메서드로 받도록 하였다.
mav.addObject("greeting",helloBean.getGreeting());
mav.addObject에 helloBean.getGreeting() 메서드를 셋팅 하였다. helloBean은 interface이고 그 interface를 사용하려면 그를 구현한 HelloBeanImpl Class를 사용하여야 할 것 인대 어떻게 interface 를 사용하였는데 HelloIBeanImpl Class의 getGreeting() 메서드의 값을 출력할 수 있었을까?
다음 xml설정을 보도록 한다.
<bean id="helloBean" class="spring.ex1.HelloBeanImpl" />
<bean name="/hello.htm" class="spring.ex1.HelloController">
<property name="helloBean" ref="helloBean" />
</bean>
위와 같이 helloBean이라는 ID 값으로 HelloBeanImpl Class를 bean으로 선언하였다. 여기서 중요한 점은 HelloController에서 새로 추가된 property 이다.
이는 name에서 HelloController bean에서 존재하는 helloBean이라는 레퍼런스 값을 가진 객체를 ref 속성을 통해 xml에 선언된 ID : helloBean의 값을 가지는 객체를 setter 메서드를 통해 주입 시켜준 다는 것이다.
setHelloBean을 통해 HelloBeanImpl 은 helloBean 레퍼런스에 대입되고, HelloBeanImpl 은 HelloBean interface를 구현 하였기 때문에 HelloBean의 getGreeting()메서드를 오버라이딩 하게 된다.
그렇게 해서 HelloBean은 새로운 값을 주입 받게 되고, 그 결과를 JSP페이지로 리턴할 수 있게 된다.
그럼 이러한 방법으로 얻을 수 있는 이득은 무엇인가?
1. 객체의 라이프 사이클을 사용자가 아닌 서버가 관리 하기에 좀더 효율적인 관리를 할 수 있다.
2. 인터페이스에 의한 느슨한 결합에 의해 변덕 스러운 클라이언트에 요구 사항에 빠르게 대처 할 수 있다.
1번의 대해서는 위에서 설명 하였고, 2번 장점에 관한 예를 간단히 보도록 한다.
새로운 Class로 HelloBeanImpl2 Class를 생성한다.
HelloBeanImpl2.java
package spring.ex1;
public class HelloBeanImpl2 implements HelloBean {
public String getGreeting(){
return "DI Change SUCCESS!!";
}
}
이제 dispatcher-servlet.xml을 다음과 같이 변경한다.
dispatcher-servlet.xml
<beans>
<bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="helloBean" class="spring.ex1.HelloBeanImpl" />
<bean id="helloBean2" class="spring.ex1.HelloBeanImpl2" />
<bean name="/hello.htm" class="spring.ex1.HelloController">
<property name="helloBean" ref="helloBean2" />
</bean>
</beans>
소스를 수정하였으면 다시 http://localhost:9090/SpringEx/hello.htm 로 접근한다.
public String getGreeting(){
return "DI Change SUCCESS!!";
}
}
이제 dispatcher-servlet.xml을 다음과 같이 변경한다.
dispatcher-servlet.xml
<beans>
<bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="helloBean" class="spring.ex1.HelloBeanImpl" />
<bean id="helloBean2" class="spring.ex1.HelloBeanImpl2" />
<bean name="/hello.htm" class="spring.ex1.HelloController">
<property name="helloBean" ref="helloBean2" />
</bean>
</beans>
소스를 수정하였으면 다시 http://localhost:9090/SpringEx/hello.htm 로 접근한다.
실제 hello.htm을 통해 접근하는 Controller는 HelloController이지만 HelloController의 소스를 수정하지 않고 결과물을 바꿀수 있었다. 이번 예제는 비교적 간단하기 때문에 별다른 장점을 느낄 수 없겠지만,만약 큰 프로젝트에서 데이터베이스를 MySQL로 이용하던 어플리케이션에서 Oracle로의 마이그레이션등 의 작업이 있다고 생각한다면 어떻게 될까? 아마 새로운 DAO 개발은 물론 모든DAO가 들어간 클래스들 에 관한 수정도 같이 작업해야 할 것이다. 허나 Spring의 DI를 이용하면, DAO만 새로 개발해주고 config 파일에서 DAO의 주입만 바꿔 주면 보다 간단히 작업을 할 수 있을 것이다.
댓글 없음:
댓글 쓰기