본문 바로가기

Spring

스프링 DI

앞서 보셨던 DaoFactory를 다시 보시겠습니다.

class DaoFactory{
    public CarDAO carDAO(){
        CarDAO carDao = new CarDAO();
        carDao.setDbConnection(dBConnection());
        
        return carDao;
    }
    
    public DBConnection getConnection(){
        return new MySqlDBConnection_real(); // 변경되는 부분
    }
}

간단합니다. CarDAO, DBConnection 오브젝트를 관리하고 있습니다. 서로 의존관계도 주입해주고요.

이러한 클래스를 IOC 컨테이너라고 하는데, 보다시피 기능이 빈약하고 매번 만들자니 번거롭습니다.

편리하게도 스프링에서 이런 IOC 컨테이너를 제공해주는 것이 있습니다. 이를 한번 사용해보겠습니다.


일단 몇가지 용어만 숙지하고 가겠습니다.

DaoFactory는 CarDAO와 DBConnection 라는 2가지 오브젝트를 관리하고 있었습니다.

관리하고 있다는 말은 생성, 관계 설정, 사용 등을 제어해줬다는 의미입니다.

DaoFactory에서 각 오브젝트들을 생성하고, 관계 설정하고 돌려줬었죠??


스프링이 제공하는 IOC 컨테이너도 DaoFactory와 동일하게 이런 오브젝트들을 관리합니다. 

그리고 스프링에서는 이렇게 관리가 되는 오브젝트들을 이라고 부릅니다.

그리고 이러한 빈들을 관리하는 IOC 컨테이너(DaoFactory같은)를 빈 팩토리 라고 부릅니다. 직관적인 이름이네요 ㅎㅎ

그리고 이 빈 팩토리를 좀 더 확장한 어플리케이션 컨텍스트 라는 것이 있고, 대부분 이를 더 자주 사용합니다.

말로만 해서는 좀 복잡네요. 코드로 한번 보겠습니다.



설정파일 작성 및 사용

빈 팩토리나 어플리케이션 컨텍스트는 위의 DaoFactory처럼 자바 코드로 구현하지 않고 설정 파일 이라는 것을 사용합니다.

설정파일을 작성하는 법은 여러가지가 있는데, 그 중 대표적인 XML을 사용해서 작성해보겠습니다.

<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="carDAO" class="spring.dao.CarDAO">
		<property name="dbConnection" ref="dbConncetion"></property>
	</bean>
	
	<bean id="dbConnection" class="spring.dao.MySqlDBConnection_real"></bean>
</beans>

<beans> 라는 태그는 해당 파일이 설정 파일이라는 것을 의미합니다.

이후 보이는 기나긴 글씨들은.. 네임스페이스를 의미합니다. <beans>, <bean> 등의 태그를 사용하기 위해 추가한 것이죠.

복사해서 사용해도 되지만, STS를 사용하시면 설정파일 생성하실 때 Spring Bean Configuration File로 생성하시면

네임스페이스들을 고르실 수 있으니 사용해보시기 바랍니다. 편리해요~~


<bean>은 어플리케이션 컨텍스트에 의해 관리되는 오브젝트를 말합니다. 

아까 스프링에서 관리되는 오브젝트는 빈 이라고 했었죠?

DaoFactory와 매칭해보면 id 속성은 메서드 명이 되겠고, class 속성은 리턴해주는 오브젝트 객체가 됩니다(패키지 포함)

<bean> 태그 하나만으로 DaoFactory에서 new를 통해 직접 오브젝트를 생성하는 과정이 대체됩니다.


그렇다면 property 속성은 무엇일까요

property 속성은 의존 오브젝트와의 관계를 정의해주는 부분입니다.

이렇게 말하면 어렵고요.ㅋㅋ 위의 setter 호출 부분이 property 태그로 대체된 것입니다.


carDao.setDbConnection(dBConnection());

이 부분이죠.

set 부분이 <property> 태그로 대체된 것이므로, 반드시 setXXX 형태로 시작하는 setter 메서드여야 합니다.

name 속성은 set을 제외한 메서드의 이름이며, ref는 주입하게 될 오브젝트의 id를 넣는 속성입니다.

결국 <property> 태그를 통한 의존관계 주입을 해주는 과정이지요.


ref 처럼 오브젝트를 넣는 속성도 있지만, 값을 주입하는 value 속성도 있습니다.

가령 CarDAO에 setCarColor(String color) 와 같은 setter가 있었다면,

<property name="carColor" value="red" /> 와 같이 값을 주입하는 것 또한 가능합니다.

value속성에 넣는 값은 기본적으로 String 형태지만, 주입하는 setter의 파라미터 타입에 따라 자동 형 변환해줍니다.

만약 setNumber(int number) 와 같은 setter 였다면, <property name="number" value="3"/> 해도 들어간다는 거죠.

그리고 일반적인 형태의 파라미터말고도 컬렉션 형태의 파라미터도 태그로 전향 가능합니다.

컬렉션 형태의 값은 속성으로 불가능하므로 직접 태그로 사용해줘야 합니다.

- <list>

setXXX(List<?> list) 형태의 setter에 값 넣을 때 사용합니다.

<property name="list">
	<list>
		<value>123</value>
		<ref bean="testClass"></ref>
	</list>
</property>

> 들어가는 값은 파라미터에 지정한 제네릭 값에 맞춰 넣어주면 됩니다. 

들어가는 값은 setter의 파라미터로 지정한 제네릭대로 자동 형 변환되어 들어갑니다. List 대신 배열도 사용 가능합니다.

위에서 <value>와 <ref>를 통시에 넣은 것은 value도 들어갈 수 있고, ref도 들어갈 수 있음을 보여드리기 위한 것이므로, 평소에는 setter 파라미터에 선언된 제네릭에 맞춰 넣어줘야 합니다.

(제네릭을 생략하면 위와 같이 넣을 수도 있습니다. setXXX(List list) 처럼 말이죠 ~~)

- <map>

setXXX(Map<?,?> map) 형태의 setter에 값 넣을 때 사용합니다.

<property name="map">
	<map>
		<entry>
			<key><value>1</value></key>
			<value>1Key</value>
		</entry>
		<entry>
			<key><value>2</value></key>
			<ref bean="class2"></ref>
		</entry>		
	</map>
</property>

> map 태그 내 entry 내에 key와 값이 들어가는 형태입니다. 

이 또한 entry 태그 내 값은 지정한 제네릭에 맞춰 넣어주면 됩니다. 이것도 <list>와 동일하게 지정한 제네릭대로 자동 형

변환되어 들어갑니다.

여기서 <value>와 <ref>를 같이 넣은 이유도 위와 동일합니다


이로써 설정파일의 작성은 끝이 났습니다.

그렇다면 IOC 컨테이너인 어플리케이션 컨텍스트는 이를 사용해야겠지요..

용어로만 얘기했던 어플리케이션 컨텍스트, 빈 팩토리는 사실 별게 아니고 그냥 스프링 제공 인터페이스 입니다.

org.springframework.beans.factory.BeanFactory;

org.springframework.context.ApplicationContext;

ApplicationContext는 BeanFactory를 상속한 인터페이스입니다. 아까 기능의 확장이라고 얘기했었죠?


그냥 클라이언트가 저 인터페이스를 사용하여 설정파일에 있는 빈 들을 가져오기만 하면 바로 사용 가능한 겁니다.

DaoFactory의 메서드를 호출해서 빈을 가져오는 과정과 별 다를 것이 없습니다.


public static void main(String[] args) throws Exception{
    ApplicationContext context = 
            new GenericXmlApplicationContext("applicationContext.xml");
    CarDAO carDao = context.getBean("carDao",CarDAO.class);
    
    System.out.println(carDao.selectSUVData());
    System.out.println(carDao.selectSedanData());
    System.out.println(carDao.selectCoupeData());
}

GenerixXmlApplicationContext는 클래스패스를 이용하여 설정파일의 위치를 지정해주면

해당 설정파일을 읽어오는 ApplicationContext 구현체입니다.

설정파일을 읽어온 뒤는 간단합니다. ApplicationContext의 getBean 메서드를 사용해서 빈을 가져오면 됩니다.

getBean의 첫번째 인자로는 설정파일에 명시된 id를 주면 되고, 두번째 인자로는 리턴 타입을 주면 됩니다.

(원래 getBean의 리턴 타입은 Object 였으나, 자바 5 이상의 제네릭을 사용하게 하는 두번째 인자를 넣어줌으로써 지저분한 캐스팅 코드가 필요없어지게 되었습니다.)


작동하는 기능만 보면 DaoFactory와 별 차이 없지만, 그것은 단면적인 부분만을 보셔서 그런것이고

스프링의 어플리케이션 컨텍스트는 DaoFactory와는 비교도 안되게 많은 장점을 제공해줍니다.



ApplicationContext의 장점

첫째로, 사용자가 굳이 컨테이너 클래스를 알 필요없이 ApplicationContext 객체만 사용하면 된다는 점입니다.

위에서 DaoFactory가 있었고, 만약 서비스 관련 오브젝트들을 관리하려면 또 ServiceFactory와 같은 컨테이너가

필요헀을 겁니다. 하지만 어플리케이션 컨텍스트를 사용함으로써 사용자는 굳이 컨테이너 클래스를 알 필요가

없어지게 되고, 각종 빈 들도 설정파일 하나에 관리할 수 있게되어 일관된 방식으로 오브젝트를 얻어올 수 있게됩니다.


둘째로, 빈을 검색하는 다양한 방법을 제공한다는 점입니다. 현재는 getBean 메서드로 빈의 id를 검색하여 가져왔지만

타입으로도 가져올 수 있고, 어노테이션으로 가져올 수도 있습니다.


셋째로, 어플리케이션 컨텍스트는 종합 IOC 서비스를 제공해줍니다. 단순히 오브젝트 의존관계를 설정하고 그를 반환해주는 것 이상의 기능을 가집니다. (싱글톤, 후처리 등등)

이는 앞으로 차근차근 알아보겠습니다 ^^


지금까지 스프링이 제공하는 IOC 컨테이너에 대해 알아보았습니다.

개인적인 생각으로 전략패턴 + IOC + DI의 조합은 정말 멋집니다..

스프링 제공 IOC 컨테이너라고 표현했지만, IOC는 여러 분야에서 폭 넓게 사용되는 용어라 스프링의 특징을 확실히 집어주진 못합니다. 

하지만 IOC 컨테이너의 기능에서 스프링의 대표적 기술인 DI를 사용하는 순간 달라집니다.

스프링이 여타 프레임워크와 차별화해서 제공해주는 기술들은 DI라는 용어를 사용할 떄 더 분명하게 드러납니다.

그리하여 스프링을 DI 컨테이너라고 더 많이 부르는 것 입니다.



여기까지 스프링 DI 포스팅입니다. 감사합니다 ~

'Spring' 카테고리의 다른 글

템플릿 콜백 패턴  (0) 2016.06.28
테스트와 JUnit  (3) 2016.06.27
싱글톤과 스프링  (0) 2016.06.24
DI란?  (0) 2016.06.16
스프링 dataSource 예제 (Mysql, Oracle)  (0) 2016.06.09