2009년 7월 9일 목요일

스프링 빈 오브젝트 가져오기 (How to get SpringBean)

스프링으로 프레임웍을 구축할때 컨텍스트에서 빈을 가져 오면
유용하게 응용을 할 수 있습니다. 예를 들어서 filter 또는 Controller 에서
사용자 인증 체크를 하거나 메뉴 카테고리를 생성거나 그 밖에 다양하게 빈을
재사용 할 수 있습니다. 먼저 소스에 대한 설명 하기 전에 스프링 빈에 대한
기본적 이해가 필요 합니다. 스프링 빈은 web application으로 사용시
기본적으로 ServletContext에서 관리 됩니다.이러한 빈을 로딩하는 방법은 두 가지
가 있습니다. Servlet Listener를 상속받은 org.springframework.web.context.ContextLoaderListener 와
Servlet을 상속 받은 org.springframework.web.servlet.DispatcherServlet 입니다.
둘다 ServletContext에서 빈이 관리 되지만 물리적으로 서로 다른 메모리 영역을
사용합니다. 즉 서로 다른 곳에서 관리 됩니다. 둘의 의존 관계는
DispatcherServlet =>ContextLoadListender를 참조하는 관계로 되어 있습니다.
즉 DispatcherServlet 로딩된 빈에서 ContextLoadListender로딩된 빈을 참조 할 수
있지만 반대일 경우는 참조를 할 수가 없습니다.
한번은 제가 filter에서 빈을 가져올일이 있어서 WebApplicationContext의 getBean()
메서드를 호출했는데 빈을 가져 올 수 없었습니다.이유는 일반 filter나 servlet에서는
DispatcherServlet 등록 빈을 참조 할 수가 없습니다. 책이나 다른 블로그에서
빈을 가져 오는 소스를 적용해도 마찬가지 였습니다.그래서 ContextLoadListender
빈을 등록했더니 빈을 가져 올수 있었습니다. 제가 문제를 접근할때 패턴이던 기술이든
간에 반드시 이유가 있다는 것입니다. 스트럿츠 처럼 ActionServlet만 만들면 되지
왜이렇게 했을까 .. 이유는 프레임웍 관점에서 보면 공통으로 사용할 빈이 있고
자신 혼자 사용할 빈이 있습니다. 무슨 얘기냐면 DispatcherServlet 이 여러개 나올 수 있는
경우 입니다. 그래서 ContextLoadListender에 등록된 빈은 공통적인 성격을 가져야
합니다. (Service,DAO,Transaction) ,DispatcherServlet에 등록된 빈은 (Controller,Validator)
이렇게 구성되어야 할 것 같습니다.
서두가 길었네요. 이제 소스에 대해서 설명 드리겠습니다.
이 메서드는 ContextLoadListender로 로딩되었던,DispatcherServlet로 로딩되었던
두 곳중 한곳에라도 존재할경우 알아서 가져오는 메서드 입니다.
메서드 로직은 단순 합니다.먼저 DispatcherServlet로 로딩된 빈을 검색 후 있으면 리턴
하고 없으면 ContextLoadListender에서 검색 후 리턴 하는 로직입니다.

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.support.RequestContextUtils;

public class SpringBeanSupport{

    public static Object getBean(final HttpServletRequest request, final String beanId) throws Exception {

        Object beanObject = null;
        ServletContext sc;
        HttpSession hs;
        WebApplicationContext webApplicationContext;

        // DispatcherServlet으로 로딩된 context를 가져 온다.
        webApplicationContext = RequestContextUtils

        .getWebApplicationContext(request);
        // 빈을 검색해서 해당 빈 오브젝트를 가져 온다.
        if (webApplicationContext.containsBean(beanId)) {
            beanObject = webApplicationContext.getBean(beanId);
            return beanObject;
        }

        hs = request.getSession();
        sc = hs.getServletContext();
        // ContextLoaderListener으로 로딩된 context를 가져 온다.
        webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(sc);

        if (webApplicationContext.containsBean(beanId)) {
            beanObject = webApplicationContext.getBean(beanId);
            return beanObject;
        }
        return beanObject;
    }
}


Example Use Case :
MemberDAOImpl memberDAOImpl =
(MemberDAOImpl )SpringBeanSupport.getBean("memberDao");

리턴 오브젝트는 Object 타입이기 때문에 반드시 호출하는 쪽에서는 캐스팅을
하셔야 합니다.

댓글 2개:

  1. getBean(final HttpServletRequest request, final String beanId)을 호출해줄때 request없이 beanId만 지정해 주셨는데;;

    서블릿이 그냥 자바 클래스에서 생성된 빈 객체를 가져오려면 어떻게 해야 하죠.. HttpServletRequest 없이요..

    답글삭제
  2. 위에글 익명으로 작성했더니 삭제가 안되네요..

    서블릿이 그냥 자바...<< 이부분은 서블릿이 아닌 별도의 자바 클래스에서 호출할 때를 말씀 드린겁니다..

    답글삭제