객체를 미리 등록해 두고 뽑아다 쓰는 방식인 제어반전컨테이너(IOC)는 스프링프레임워크의 핵심기능 중 한 가지다.


◆ 제어 반전 컨테이너 ( IOC Container )

 DI( Dependency Injection : 의존성 주입 )라는 개념을 사용해 Spring을 이용해 어플리케이션 제작을 하게 되면, 보통 객체를 직접적으로 생성하지 않고, 등록시켜 두고 뽑아다 쓰는 형태로 코드가 구현됨.
( 스프링의 객체 관리 기능 )
따라서 코드 자체를 수정하지 않고 등록시켜 둔 설정만 수정해 주면 되는 방식으로 돌아감.

 IOC ( Inversion Of Controll / 제어 반전) Contaner
즉, 제어 반전 컨테이너는 등록 시킬 객체들을 저장 하고 있는 컨테이너개념


▶ IOC컨테이너 객체등록


1. spring bean Configuration File 생성

IOC컨테이너의 객체들은 spring bean Configuration File(xml파일)에서 등록시킨다.
JavaResource경로 아래 생성시킨다.


2. 설정파일에서 <bean>태그로 객체를 하나씩 등록

<bean> 태그는 객체를 IOC컨테이너에 등록시키는 태그이다.

  • id : 객체를 불러올 때 쓰일 아이디
  • class : 해당 객체의 클래스파일 경로
<?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.xsd">
    <bean id="list" class="java.util.ArrayList"></bean>
    <bean id="map" class="java.util.HashMap"></bean>
</beans>

※ 인터페이스나 추상클래스는 등록이 불가하고, 기본적으로 등록되어있는 객체들도 있다.


▶ 객체 불러오기 ( 객체 주입 )

1. ApplicationContext객체 생성

코드에서 IOC컨테이너에 등록시킨 객체들을 불러오기 위해서 ApplicationContext객체를 이용하여 설정 파일의 경로를 잡고 이 객체를 이용하여 등록시킨 객체의 id또는 객체타입으로 객체를 불러올 수 있다.

  • 선언
    ApplicationContext 변수명
  • 생성
    new ClassPathXmlApplicationContext(“spring bean configuration File경로”);
ApplicationContext ctx =
new ClassPathXmlApplicationContext("spring/alpha/alpha-config.xml");


2. ApplicationContext객체를 이용하여 객체 주입받기

등록된 객체는 ApplicationContext객체.getBean() 메서드를 이용하여 받아오고(주입), 인자에 따라 받아오는 방식이 달라진다.


※ id를 통해서 받기

등록한 객체의 아이디를 인자로 하게 되면 해당 아이디에 해당하는 객체를 뽑을 수 있다.
단, 아이디를 통해서 객체를 주입 받을 시 캐스팅이 필요하다.

Map map = (Map)ctx.getBean("map");
// => id 없거나, 잘못된 casting  에러


※ type을 통해서 받기

인자로 객체타입.class를 하게 되면, 캐스팅 없이 해당 클래스 타입에 해당하는 객체를 바로 주입받을 수 있다.

List list = ctx.getBean(List.class);

해당 객체 또는 그 하위 객체가 등록되어있지 않거나, 해당 객체가 여러 개 또는 해당 객체 타입의 하위클래스가 여러 개 등록 돼있을 시 에러
=> 또한, IOC컨테이너 안에 기본적으로 포함되어 있는 객체들이 있기 때문에, Map.class등은 안될 수 있다.


※ 캐스팅 없이 id로 받기

첫 번째 인자로 아이디, 두 번째 인자로 해당 객체의 타입.class를 넘기게 되면 캐스팅을 하지 않고 바로 객체를 뽑을 수 있다.

FileOutputStream fos = ctx.getBean("fos", FileOutputStream.class);


▶ ApplicationContext객체의 그 외 메서드

  • 등록된 객체의 개수 반환
    ctx.getBeanDefinitionCount();
  • 등록된 객체의 id를 String배열로 반환
String[] names = ctx.getBeanDefinitionNames();
for (String name : names) {
    System.out.println("=> " + name);
}

※ 위 메서드에서 반환하는 것은 자동적으로 등록되어있는 것을 제외한 설정 파일에서 등록된 객체의 수만 체크한다.


IOC컨테이너에 등록된 객체들은 기본적으로 컴파일 시 모두 자동적으로 생성자가 호출되면서 객체가 생성된다.
따라서 ApplicationContext객체로 주입 받은 객체들은 이미 생성된 객체들을 주입 받은 것이므로, 여러 개를 주입 받아와도 모두 같은 객체이다.


◆ 객체 생성자의 매개변수 설정

객체 등록 후 생성자의 매개변수를 설정하지 않을 경우 (인자가 없는)기본 생성자가 호출되면서 객체를 생성하기 때문에, 매개변수를 설정하지 않을 때 기본 생성자가 없을 경우 등록이 불가능하다.


▶ 기본형 타입의 인자가 있는 생성자를 가지고 있는 객체 등록

기본 생성자가 없고 인자가 있는 생성자만 있는 경우 생성자의 인자를 설정하지 않으면 등록이 불가능하다.
생성자의 매개변수는 <bean>태그 안에서 <constructor-arg> 태그를 이용하여 name과 value로 인자를 설정할 수 있다. ( 단, 인자로는 기본형데이터 + String 만 가능하다 )

  • name

    : 생성자의 인자 name( 자동완성(ctrl+spacebar)으로 확인 가능 )

  • value

    : 인자 값

class Target{ public Target(int flag) { ~ } }
// =>  처럼 Target클래스를 설계하엿을 ,
<bean id="target" class="spring.beta.Target">
    <constructor-arg name="flag" value="3"></constructor-arg>
</bean>

( value값은 자동적으로 인자의 데이터 형에 맞게 변환되어 들어간다.)


▶ (기본형타입이 아닌)객체를 인자로 하는 생성자의 경우

기본형이나 String을 인자로 하지 않는 객체의 경우는 value옵션이 아닌 ref옵션으로 등록을 해야 한다.
또한 ref에는 매개변수로 할 객체의 id값이 들어가기 때문에 인자로 등록시킬 객체 또한 미리 등록을 시켜 둬야한다.


Ex) File객체를 인자로 하는 FileOutputStream일 경우

  1. File 객체 등록
<bean id=f class=java.io.File/>
  1. FileOutputStream객체 등록 및 생성자 인자설정
<bean id=fos class=java.io.FileOutputTsream>
    <constructor-arg name="file" ref="f">
</bean>


▶ value와 ref는 따로 설정할 수도 있다

※ value

<bean id="target" class="spring.beta.Target">
    <constructor-arg name="flag">
        <value>3</value>
    </constructor-arg>
</bean>

※ ref

<bean id="fos" class="java.io.FileOutputStream">
    <constructor-arg name="file">
        <ref bean="f"></ref>
    </constructor-arg>
</bean>

=> ref는 bean옵션을 이용하여 등록

  • 태그를 설정할 때 같은 다른 타입의 같은 변수명을 사용하는 여러 개의 생성자가 있다면 type도 같이 설정해 줄 수도 있다.
  • 인자 값으로 null을 넣을 때 null값은 value가 아닌 <null/>태그를 이용하여 사용한다.
 
<constructor-arg name="flag"> 
    <null /> 
</constructor-arg>


◆ 컬렉션을 인자로 사용할 때 설정

생성자의 인자로 기본형데이터나 String을 사용할 때는 value를 사용하고, 일반 객체를 사용할 때는 ref를 사용하지만, 컬렉션을 인자로 하는 경우 사용하는 방식이 다르다. 이는 생성자에만 적용되는 것은 아니다.


▶ List를 인자로 하는 생성자의 벨류값 설정

List는 <list>태그와 <value>태그를 사용하여 값을 넣는다.
( <list>태그의 디폴트는 ArrayList )

<bean id="earth" class="spring.beta.elm.Earth">
    <constructor-arg name="list">
        <list>
            <value>classpath:/board-mapper.xml</value>
            <value>classpath:/account-mapper.xml</value>
        </list>
    </constructor-arg>
</bean>


▶Set을 인자로하는 생성자의 벨류값 설정

Set은 <set>태그와 <value>태그를 이용하여 값을 넣는다.
( <set>태그의 디폴트는 LinkedHashSet )
( Set의 특성상 중복 값은 허용하지 않기 때문에 같은 값은 여러번 넣어도 효과가 없다. )

<bean id="warter" class="spring.beta.elm.Water">
    <constructor-arg name="set">
        <set>
            <value>192</value>
            <value>192</value>
            <value>168</value>
        </set>
    </constructor-arg>
</bean>

※ Set과 List는 같은 계열이기 때문에 set으로 만들어도 list로 받을 수 있고 반대의 경우도 가능하지만, Map으로는 캐스팅이 불가하다.


▶ Map을 인자로 하는 생성자의 벨류값 설정

Map은 <map>태그와 <entry>태그를 이용하여 값을 설정하고,키와 벨류 값들은 태그의 key, value옵션으로 설정한다. ( Map의 디폴트는 LinkedHashMap )

<bean id="fire" class="spring.beta.elm.Fire">
    <constructor-arg name="map">
        <map>
            <entry key="nick" value="luffy"></entry>
            <entry key="auth" value="false" value-type="boolean" />
            <entry key="file" value-ref="f" />
        </map>
    </constructor-arg>
</bean>


▶ Properties를 인자로 하는 생성자의 벨류값 설정

설정은 <props> 태그 안에 <prop key="키">벨류값</prop>으로 한다.

<bean id="wind" class="spring.beta.elm.Wind">
    <constructor-arg>
        <props>
            <prop key="auth">true</prop>
            <prop key="nick">admin</prop>
        </props>
    </constructor-arg>
</bean>

※ Properties ?

Properties는 <String,String>형태의 특수 맵이다. 이 객체를 쓰는 이유는 setProperty(String, String),getProperty(String, String)메서드를 사용하기 위해 쓰인다.
( getProperty(키,디폴트) : 키가 없을 경우 디폴트값이 출력되도록 하는 메서드 )

  • Property와 Map또한 같은 계열의 객체이기 때문에 캐스팅이 가능하다. 하지만, 이 역시 객체에 맞게 쓰는 것을 권장한다.

  • IOC컨테이너의 등록한 객체를 컬렉션 객체에 넣을 때는 value-ref옵션을 이용하여 등록한 객체의 아이디 값을 설정한다.

  • value-type으로 벨류 값의 타입을 지정할 수도 있다.