Spring Framework IoC

2015. 11. 24. 21:42Framework/Spring Framework

    목차

3. Spring IoC


제어 역행(Inversion of Control) 패턴의 기본 개념은 객체를 구현하지 않고 구현되는 방법을 기술하는 것이다. 컴포넌트와 서비스들을 코드에 직접 연결하지는 않지만, 설정 파일에서 어떤 컴포넌트가 어떤 서비스를 요구하는지를 기술한다. 컨테이너(이 경우, Spring 프레임워크, IOC 컨테이너)는 이 모든 것을 연결한다.

전형적인 IOC 시나리오에서, 컨테이너는 모든 객체들을 만들고 필요한 속성들을 설정하여, 이들을 한데 연결하고 메소드가 언제 호출될 것인지를 결정한다. IOC의 이 세 가지 구현 패턴을 아래 테이블에 정리했다.





Spring 프레임워크는 IOC 컨테이너 구현에 Type 2와 Type 3 구현을 사용한다.


3.1. IoC란?


객체의 생성에서부터 생명주기의 관리까지 모든 객체에 대한 제어권이 바뀌었다는 것을 의미한다. 즉, 컨테이너 기능을 내부에 포함하고 있다. POJO 기반으로 애플리케이션 개발을 진행하지만 POJO를 관리할 수 있는 컨테이너를 만들어서 EJB 컨테이너에서 지원하던 기능들을 지원하는 것이다.





1) DL(Dependency Lookup) 혹은 DP(Dependency Pull)

모든 IoC 컨테이너는 각 컨테이너에서 관리해야 하는 객체들을 관리하기 위한 별도의 저장소를 가진다.

Servlet 컨테이너 : web.xml, Spring 컨테이너 : applicationContext.xml Bean에 접근하기 위하여 컨테이너에서 제공하는 API를 이용하여 사용하고자 하는 Bean을 Lookup 하는 것.


public class UserServiceHelper {

    private static final String USERSERVICE_BEANID = "userService";


    public static UserService getUserService(ServletContext ctx) {

        WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(ctx);

        return (UserService) wac.getBean(USERSERVICE_BEANID);

    }

}


이 방법은 컨테이너 API와 의존관계를 많이 가지면 가질수록 어플리케이션 컨테이너에 종속되는 단점이 있다.


2) DI(Dependency Injection)

DI는 Spring에서 새롭게 지원하는 IOC의 한 형태이다.

각 계층 사이, 각 class 사이에 필요로 하는 의존관계가 있다면 이를 컨테이너가 자동 적으로 연결시켜 주는 것. 이 방법은 컨테이너 API와 의존관계를 최소화 하거나 없앨수 있는 장점이 있다. 객체의 생성에서부터 생명주기의 관리까지 모든 객체에 대한 제어권이 바뀌었다는 것을 의미한다. 즉, 컨테이너 기능을 내부에 포함하고 있다는 말이 된다. 

DI는 각 class 사이의 의존관계를 Bean 설정 정보를 바탕으로 컨테이너가 자동적으로 연결해 주는 것이다. 

개발자들은 단지 Bean 설정 파일(저장소 관리 파일)에서 의존관계가 필요하다는 정보를 추가하기만 하면 된다.


① Setter Injection

class 사이의 의존관계를 연결시키기 위해 setter 메소드를 이용하는 방법.


public class UserServiceImpl implements UserService, InitializingBean {

    private UserDAO userDAO;


    public void setUserDAO(UserDAO newUserDAO) {

        this.userDAO = newUserDAO;

    }


    public int addUser(User user) throws ExistedUserException {

        if (userDAO.existedUser(user.getUserId())) {

            throw new ExistedUserException(context.getMessage(

                                                            "user.existed.exception",

                                                            new Object[] { user.getUserId() }, null));

        }


        int result = userDAO.insert(user);

        return result;

    }


    ..........추가 코드...........


}


코드를 보면 퍼시스턴스 계층과의 통신을 위해 UserDAO 인터페이스와 의존관계가 형성된다.

소스코드 어디에도 UserDAO 구현 class의 정보와 인스턴스가 생성되는 곳이 없다. 

그러나 위 class는 UserDAO 인스턴스가 존재한다는 가정하에 개발을 진행하고 있는데 이것이 가능한 이유는 Spring 프레임워크의 빈 설정파일에 있다.


<?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="userDAO" class="com.xxx.user.dao.MySQLUserDAO">

    <bean id="userService" class="com.xxx.user.service.UserServiceImpl">

        <property name="userDAO">

            <ref local="userDAO"/>

        </property>

    </bean>

</beans>


② Constructor Injection

class 사이의 의존관계를 연결시키기 위해 생성자를 이용하는 방법.

위 소스를 Constructor Injection으로 변경하면 다음과 같다.


public class UserServiceImpl implements UserService, InitializingBean {

    private UserDAO userDAO;


    public UserServiceImpl (UserDAO newUserDAO) {

        this.UserDAO = newUserDAO;

    }


    public int addUser(User user) throws ExistedUserException {

        if (userDAO.existedUser(user.getUserId())) {

             throw new ExistedUserException(context.getMessage(

                                          "user.existed.exception",

                                          new Object[] { user.getUserId() }, null));

        }


        int result = userDAO.insert(user);

        return result;

    }


    ..........추가 코드...........


}


<?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="userDAO" class="com.xxx.user.dao.MySQLUserDAO">

    <bean id="userService" class="com.xxx.user.service.UserServiceImpl">

        <constructor-arg>

            <ref local="userDAO"/>

        </constructor-arg>

    </bean>

</beans>


③ Method Injection

Method Injection은 Setter Injection과 Constructor Injection이 가지고 있는 한계점을 극복하기 위하여 Spring 프레임워크 1.1 버전에서 새롭게 지원하고 있는 DI의 한 종류이다. Singleton 인스턴스와 Non Singleton 인스턴스의 의존관계를 연결할 필요가 있을 때 사용한다.


※ DI를 이용하여 Spring과 타 Framework의 연동이 가능하여 소스의 재사용을 증가시킨다.

    Spring은 프레임워크나 컨테이너에 종속되지 않도록 어플리케이션을 개발하는 것이 가능하다.


3.2. Bean 생명주기 관리


Spring Bean의 생명주기를 이해하는 것이 중요한 이유는, Spring이 Bean의 생성방법을 커스터마이징 할 수 있는 기회를 제공하기 때문이다.





1) BeanFactory / ApplicationContext

Spring 프레임워크는 Bean 설정 파일에 정의되어 있는 설정 정보를 초기화하고 초기화된 빈에 접근하기 위한 목적으로 BeanFactory 와 ApplicationContext 인터페이스를 가지고 있다. Bean 설정파일에 정의되어 있는 정보를 바탕으로 기본적인 Bean의 생성, 초기화, 접근성과 같은 기능을 제공하는 인터페이스는 BeanFactory이다.

ApplicationContext는 BeanFactory의 하위 인터페이스로서 BeanFactory가 가지고 있는 기능 외에 추가적인 기능을 지원한다.

ApplicationContext 추가적으로 지원하는 기능으로는,


① MessageSource를 지원하여 어플리케이션에서 필요한 메시지들을 관리.

② Spring 프레임워크에서 관리하는 POJO Bean들 중 ApplicationListener를 구현하여 설정 파일에 등록할 경우 등록된 리스너에게 이벤트를 전달하는 것이 가능하다.

즉, ApplicationContext를 이용할 경우 어플리케이션 내에 간단한 이벤트 기능을 구현하는 것이 가능하다.

③ 어플리케이션 내에서 파일, URL과 같은 다양한 자원에 접근할 수 있다.

ResourceLoader를 지원하므로 다양한 하위레벨의 자원을 Spring 프레임워크와 Resource 인스턴스로 생성하는 것이 가능하다. ApplicationContext가 제공하는 추가적인 기능 때문에, 대부분의 어플리케이션에서는 BeanFactory보다 선호하는 경향이 강하다. BeanFactory의 사용을 고려해볼 만한 유일한 경우는 이동통신기기와 같이 자원이 한정적인 상황일 때 이다. 또한, BeanFactory는 모든 Bean을 늦게 로딩(lazy loading)하며 ApplicationContext는 미리 로딩(preloading)한다.

미리 로딩한다는 것은 해당 Bean이 필요할 때 즉시 사용될 수 있도록 보장해주며 Bean이 생성되기를 기다릴 필요가 없다는 의미이다.


2) ApplicationContext 대표 구현 class

① ClassPathXmlApplicationContext

class 경로에 있는 XML 파일로부터 컨텍스트 정의를 로딩하며, 컨텍스트 정의를 클래스 경로에 있는 자원으로 취급한다.

② FileSystemXmlApplicationContext

파일 시스템에 있는 XML 파일로부터 컨텍스트 정의를 로딩한다.

③ XmlWebApplicationContext

웹 어플리케이션에 포함되있는 XML 파일로부터 컨텍스트 정의를 로딩한다

3) 생성자와 세터 비교

① 생성자 주입 장점

- 생성자 주입은 강한 의존성 계약을 강제한다.

- Bean의 모든 의존성이 생성자를 통해 설정되기 때문에, 불필요한 세터 메소드를

가질 필요가 없다.

- 오직 생성자를 통해서만 특성을 설정할 수 있도록 함으로써, 자연히 그 특성은 변경

되지 않는 특성이 되는 효과가 있다.

② 세터 주입 장점

- Bean이 여러개의 의존성을 갖는 경우에는 생성자의 파라미터 목록이 매우 길어진다.

- 유효한 객체를 구성하는 다양한 방법이 존재한다면, 오직 파라미터의 수와 타입에 의해 생성자의 시그너처가 다양해질 것이므로 특정한 생성자를 식별하기가 어려워진다.

- 생성자가 동일한 타입의 두 개 이상의 파라미터를 취급한다면, 각 파라미터의 목적을 파악하기 어려워질 것이다.

- 생성자 주입은 그 자체로 즉시 상속에 사용할 수 없다. 부모 객체의 private 특성을 설정하기 위해서는 Bean의 생성자에서 항상 파라미터를 super()로 넘겨야 한다.  



[출처] - 이 종 화 (ingenuity.egloos.com) -

'Framework > Spring Framework' 카테고리의 다른 글

Spring Framework form 처리  (0) 2015.12.03
Spring Framework MVC  (0) 2015.12.01
Spring Framework Bean 설정 파일 관리  (0) 2015.11.27
Spring Framework AOP  (0) 2015.11.26
Spring Framework 개요  (0) 2015.11.24