기본 콘텐츠로 건너뛰기

BeanFinder for Spring

SPRING FRAMEWORK

Spring Context 외부에서 Bean 주입받기: ServletContext와 BeanFinder 유틸리티

Filter, Listener, 혹은 레거시 코드 등 Spring 컨테이너의 제어를 받지 않는 곳에서 스프링 빈(Bean)을 사용해야 할 때가 있습니다. ServletContext를 활용해 ApplicationContext에 접근하고 빈을 동적으로 조회하는 BeanFinder 유틸리티 구현 방법을 알아봅니다.

1. 문제 상황: Spring 관리 영역 밖에서의 빈 접근

Spring MVC 패턴 안에서는 @Autowired나 생성자 주입을 통해 손쉽게 빈을 사용할 수 있습니다. 하지만 서블릿 필터(Filter), 리스너(Listener), 또는 일반 POJO 클래스에서는 이러한 자동 주입(DI)이 동작하지 않습니다.

이러한 상황에서는 ServletContext를 통해 Spring의 루트 컨테이너인 WebApplicationContext를 직접 찾아내고, 필요한 빈을 수동으로 꺼내오는 전략이 필요합니다.

2. 핵심 구현: BeanFinder 유틸리티 클래스

아래 코드는 WebApplicationContextUtils를 사용하여 컨텍스트에 접근하고, ID 또는 Class 타입을 기반으로 빈을 검색하는 유틸리티 클래스입니다.

import javax.servlet.ServletContext;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

public class BeanFinder {
 
    // 1. ServletContext로부터 Spring ApplicationContext 획득
    private static ApplicationContext getApplicationContext(ServletContext context) {
        return WebApplicationContextUtils.getWebApplicationContext(context);
    }
 
    // 2. Bean ID로 조회
    public static Object getBean(ServletContext context, String id) {
        return getApplicationContext(context).getBean(id);
    }
 
    // 3. Class 타입으로 조회
    public static  T getBean(ServletContext context, Class clazz) {
        ApplicationContext appContext = getApplicationContext(context);
        return appContext.getBean(clazz);
    }
    
    // (옵션) 타입으로 조회 시 정확한 매칭을 위한 헬퍼 메소드
    public static String getBeanName(ApplicationContext context, Class clazz) {
        for (String beanName : context.getBeanNamesForType(clazz)) {
            // 프록시 객체 등을 고려하여 원본 클래스명 비교
            if (getBeanClassName(context, beanName).equals(clazz.getName())) {
                return beanName;
            }
        }
        return null;
    }
 
    // AOP Proxy 처리를 위한 클래스명 파싱
    private static String getBeanClassName(ApplicationContext context, String beanName) {
        String className = context.getBean(beanName).getClass().getName();
        int pos = className.indexOf("$$"); // CGLIB Proxy 식별
        
        return pos == -1 ? className : className.substring(0, pos);
    }
}

3. 주요 로직 상세 분석

위 코드에서 눈여겨봐야 할 핵심 기술 요소는 다음과 같습니다.

  • WebApplicationContextUtils: Spring이 제공하는 유틸리티로, 서블릿 컨테이너가 시작될 때 ServletContext 속성(Attribute)에 저장해둔 Spring 컨테이너 객체를 꺼내옵니다.
  • 프록시(Proxy) 처리: Spring AOP나 트랜잭션이 적용된 빈은 원본 클래스가 아닌 프록시 객체(예: UserService$$EnhancerBySpringCGLIB)로 래핑됩니다.

따라서 클래스 타입으로 빈을 조회할 때, $$ 키워드를 통해 프록시 여부를 확인하고 원본 클래스 이름을 추적하는 로직이 포함되어야 정확한 조회가 가능합니다.

4. 실무 활용 시나리오

BeanFinder 클래스는 다음과 같은 상황에서 매우 유용하게 사용됩니다.

  • Legacy JSP 마이그레이션: Spring 컨트롤러를 거치지 않고 직접 호출되는 레거시 JSP 파일에서 Service 빈을 호출해야 할 때.
  • Servlet Filter: 인코딩이나 보안 처리를 담당하는 필터 내에서 DB 접근이 필요한 DAO/Repository 빈을 사용해야 할 때.
  • Third-party 라이브러리 연동: Spring을 인지하지 못하는 외부 라이브러리의 콜백 함수 내부.

💡 Tip: Spring 4.3 이상을 사용한다면 컴포넌트 내부에서는 생성자 주입이 권장되지만, 컨테이너 외부에서의 접근에는 이 방식이 가장 확실한 솔루션입니다.

댓글

이 블로그의 인기 게시물

Java Servlet Request Parameter 완전 정복 — GET/POST 모든 파라미터 확인 & 디버깅 예제 (Request Parameter 전체보기)

Java Servlet Request Parameter 완전 정복 — GET/POST 모든 파라미터 확인 & 디버깅 예제 Java Servlet Request Parameter 완전 정복 웹 애플리케이션에서 클라이언트로부터 전달되는 Request Parameter 를 확인하는 것은 필수입니다. 이 글에서는 Java Servlet 과 JSP 에서 GET/POST 요청 파라미터를 전체 출력하고 디버깅하는 방법을 다양한 예제와 함께 소개합니다. 1. 기본 예제: getParameterNames() 사용 Enumeration<String> params = request.getParameterNames(); System.out.println("----------------------------"); while (params.hasMoreElements()){ String name = params.nextElement(); System.out.println(name + " : " + request.getParameter(name)); } System.out.println("----------------------------"); 위 코드는 요청에 포함된 모든 파라미터 이름과 값을 출력하는 기본 방법입니다. 2. HTML Form과 연동 예제 <form action="CheckParamsServlet" method="post"> 이름: <input type="text" name="username"><br> 이메일: <input type="email" name="email"><b...

PostgreSQL 달력(일별,월별)

SQL 팁: GENERATE_SERIES로 일별, 월별 날짜 목록 만들기 SQL 팁: GENERATE_SERIES 로 일별, 월별 날짜 목록 만들기 데이터베이스에서 통계 리포트를 작성하거나 비어있는 날짜 데이터를 채워야 할 때, 특정 기간의 날짜 목록이 필요할 수 있습니다. PostgreSQL과 같은 데이터베이스에서는 GENERATE_SERIES 함수를 사용하여 이 작업을 매우 간단하게 처리할 수 있습니다. 1. 🗓️ 일별 날짜 목록 생성하기 2020년 1월 1일부터 12월 31일까지의 모든 날짜를 '1 day' 간격으로 생성하는 쿼리입니다. WITH date_series AS ( SELECT DATE(GENERATE_SERIES( TO_DATE('2020-01-01', 'YYYY-MM-DD'), TO_DATE('2020-12-31', 'YYYY-MM-DD'), '1 day' )) AS DATE ) SELECT DATE FROM date_series 이 쿼리는 WITH 절(CTE)을 사용하여 date_series 라는 임시 테이블을 만들고, GENERATE_SERIES 함수로 날짜를 채웁니다. 결과 (일별 출력) 2. 📅 월별 날짜 목록 생성하기 동일한 원리로, 간격을 '1 MONTH' 로 변경하면 월별 목록을 생성할 수 있습니다. TO...

CSS로 레이어 팝업 화면 가운데 정렬하는 방법 (top·left·transform 완전 정리)

레이어 팝업 센터 정렬, 이 코드만 알면 끝 (CSS 예제 포함) 이벤트 배너나 공지사항을 띄울 때 레이어 팝업(center 정렬) 을 깔끔하게 잡는 게 생각보다 어렵습니다. 화면 크기가 변해도 가운데에 고정되고, 모바일에서도 자연스럽게 보이게 하려면 position , top , left , transform 을 정확하게 이해해야 합니다. 이 글에서는 아래 내용을 예제로 정리합니다. 레이어 팝업(center 정렬)의 기본 개념 자주 사용하는 position: absolute / fixed 정렬 방식 질문에서 주신 스타일 top: 3.25%; left: 50%; transform: translateX(-50%) 의 의미 실무에서 바로 쓰는 반응형 레이어 팝업 HTML/CSS 예제 1. 레이어 팝업(center 정렬)이란? 레이어 팝업(레이어 팝업창) 은 새 창을 띄우는 것이 아니라, 현재 페이지 위에 div 레이어를 띄워서 공지사항, 광고, 이벤트 등을 보여주는 방식을 말합니다. 검색엔진(SEO) 입장에서도 같은 페이지 안에 HTML이 존재 하기 때문에 팝업 안의 텍스트도 정상적으로 인덱싱될 수 있습니다. 즉, “레이어 팝업 센터 정렬”, “레이어 팝업 만드는 방법”과 같이 관련 키워드를 적절히 넣어주면 검색 노출에 도움이 됩니다. 2. 질문에서 주신 레이어 팝업 스타일 분석 질문에서 주신 스타일은 다음과 같습니다. <div class="layer-popup" style="width:1210px; z-index:9001; position:absolute; top:3.25%; left:50%; transform:translateX(-50%);"> 레이어 팝업 내용 <...