- Servlet context와 Root context
- 컨텍스트 분리 전략
- Servlet context와 Root context 차이점과 계층구조를 이해한다.
- 컨텍스트 분리 전략을 이해하여 구현한다.
- DispatcherServlet - 웹의 요청을 최초로 접수
- DispatcherServlet - 설정파일을 이용해서 ServletContext(스프링 컨테이너) 로딩
- Spring-MVC와 관련 있는 빈을 설정
- Spring-MVC와 분리되어 빈을 관리하고 싶을 때 사용
- 일반적으로 Servlet context와 Root context 분리하여 운용
지금까지 우리는 하나의 컨텍스트(컨테이너)만 사용해오며 실습을 해왔습니다. 그렇지만 일반적으로 개발할 때는 스프링 컨테이너(컨텍스트)를 두 가지로 분리해서 사용합니다. 바로 Servlet context와 Root context로 분리해서 사용합니다. 어떻게 분리하고 분리 전략은 어떤지 살펴보겠습니다.
지금까지 사용한 컨텍스트는 Servlet context입니다. 웹의 모든 요청을 최초로 받는 게 DispatcherServlet이었습니다. 필요한 빈들을 이 Servlet에 담겨서 사용되었습니다.
DispatcherServlet는 여러 개를 두고 사용할 수도 있습니다. 대표적으로 open API를 사용할 때 API 요청에 따른 DispatcherServlet를 따로 두어 요청을 처리할 수 있습니다. 이렇게 여러 개를 두면 각 DispatcherServlet는 빈들을 공유하지 못하기 때문에, 곤란한 경우가 종종 생깁니다.
그래서 Root Servlet를 만들었습니다. 여기에는 Service 빈, Repository 빈, Databasae 관련 빈을 등록하고 처리합니다.
반면에 Servlet Context는 Web과 관련된 빈들을 등록하고 사용합니다.
Servlet WebApplicationContext가 우리가 그동안 실습했던 컨텍스트입니다.
화살표 방향이 단반향입니다. Servlet Context에서 Root로 참조는 가능하지만, 반대로 참조는 불가능합니다.
Servlet에는 모든 Controller들을 등록시킵니다.
Root Context는 Web과 관련되지 않은 빈들을 등록시킵니다.
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/root-context.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
Root Context를 사용하려면 설정이 필요합니다.
설정하면서 지정한 경로에 root-context.xml를 생성해주면 됩니다.
listener 태그는 별로의 컨테이너 즉, Root Context를 띄우는 역할을 해줍니다.
- 전체 계층구조에서 최상단에 위치한 컨텍스트
- 서로 다른 서블릭 컨텍스트에서 공유해야 하는 Bean들을 등록해놓고 사용할 수 있다
- 웹 어플리케이션 전체에 적용 가능한 DB 연결, 로깅기능 등에 이용
- Servlet Context에 등록된 Bean 이용 불가능
- Servlet Context와 동일한 Bean이 있을 경우 Servlet Context Bean이 우선된다.
- 하나의 컨텍스트에 정의된 AOP 설정은 다른 컨텍스트의 빈에는 영향을 미치지 않음
Root Context에 대해 더 자세히 알아보겠습니다.
- 서블릿에서만 이용되는 컨텍스트
- 타 서블릿과 공유하기 위한 Bean들은 루트 웹 어플리케이션 컨텍스트에 등록해놓고 사용해야 함
- DispatcherServlet은 자신만의 컨텍스트를 생성, 초기화하고 동시에 루트 어플리케이션 컨텍스트를 찾아서 자신의 부모 컨텍스트로 사용
*@Controller
<context:component-scan base-package="kr.co.acomp.hello" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan>
- @Service - 트랜잭션 적용(with AOP)
- @Repository
<context:component-scan base-package="kr.co.acomp.hello"> <context:exclude-filter type="annotation" expression="org.springfrramework.stereotype.Controller" /> </context:component-scan>
기본적인 전략은 빈들이 중복되지 않게 정교하게 분리해야합니다. 먼저 Controller쪽 빈들은 Servlet Context에 등록합니다. 위에 코드 보시면 "context:include-filter" 이 태그에서 "Controller" 어노테이션을 등록하겠다라는 것입니다.
Root Context에는 "exclude"를 사용하시면 됩니다. 즉, 저 코드 내용은 "Controller 어노테이션 빈들은 제외(exclude)시키겠다" 라는 의미입니다. 그럼 서로 겹치지 않게 등록이 가능하겠죠?
↑ 먼저, web.xml에 Listener와 Root Context xml 파일을 등록합니다.
↑ root-context.xml를 생성하고 이전 실습에서 servlet-context.xml에 작성했던 DB 설정 태그들을 잘라서 root-context.xml에 붙여줍니다.
↑ 그리고 @Controller를 제외한 Bean auto-scan을 위해 < context:component-scan> 태그를 작성합니다.
↑ servlet-context.xml로 돌아가서 이번엔 @controller만 스캔하겠다고 명시해줍니다.
이렇게 context를 분리했습니다.
확인을 위해 spring explorer를 보겠습니다.
↑ 우측 상단에 'spring' 아이콘을 클릭해주세요.
↑ 우측 하단에 spring explorer가 생겼습니다.
↑ 빈들이 분리되어 있는 것을 확인할 수 있습니다.
↑ web.xml에서 새로운 root context 파일을 등록합니다.
실질적으로는 하나의 컨테이너에 올라갑니다.
↑ datasource.xml를 생성하고 root context에 있던 dataSource 태그를 옮겨보겠습니다. 또 옮기네요 ㅎ하
↑ 저장 후, 다시 spring explorer를 확인해보면, 새로운 context가 생성되어 있고 datasource Bean도 옮겨져 있네요.