* 개요
서블릿을 먼저 공부하고, 스프링 구조를 공부하면서, DispatcherServlet이 중요한 역할을 한다는 것을 알게되었습니다. 이 서블릿 덕분에 클라이언트의 요청을 스프링이 처리할 수 있으므로 그 동작과정과 작동원리가 궁금했습니다. 또한 이와 관련하여 WebApplicationContext와 ContetLoaderListener도 함께 공부했습니다.
Web Application에는 1개의 ServletContext가 있습니다. 여기에는 여러 자원들의 공유자원들이 있습니다.
WebApplicationContext는 ServletContext 안에 만들어 집니다.
ContextLoadListener는 Root WebApplicationContext에 여러 자바 빈들을 적재합니다. 주로 Service, Repository 계층 클래스와 MyBatis, DataSource 등등이 있습니다.
ContextLoaderListener는 설정파일의 위치가 지정되어 있지 않으면 기본으로 applicationContext.xml 파일에 자바 빈들을 WebApplicationContext에 적재합니다.
DispatcherServlet은 Child WebApplicationContext에 여러 자바 빈들을 적재합니다. 자식이기 때문에, 여러개를 만들 수 있습니다. 또한 Root WebApplicationContext에 접근이 가능합니다. 주로 Controller, ViewResolver, LocaleResolver 같은 Web Layer를 적재합니다.
DispatcherServlet은 설정파일의 위치가 지정되어 있지 않으면 기본으로 WEB-INF/Servlet이름-servlet.xml 파일에 있는 빈들을 WebApplicationContext에 적재합니다.
* DispatcherServlet
- HttpServlet을 상속받은 서블릿으로, 웹 응용프로그램의 web.xml에 정의합니다.
- DispatcherServlet이 로드되면, contextConfigLocation 파라미로 설정파일이 지정되어 있지 않다면, WEB-INF/서블릿이름-servlet.xml 파일에 정의된 빈들을 WebApplicationContext에 로딩합니다. 그 후, servlet-mapping을 통해 다루어질 URL을 지정합니다.
<web-app>
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value><!--설정파일의 위치--></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>example</servlet-name>
<url-pattern>/example/*</url-pattern>
</servlet-mapping>
</web-app>
<init-param>은 생략이 가능하며, 생략 시 설정파일의 이름은 /WEB-INF/example-servlet.xml입니다.
<load-onstartup> 값이 1이기 때문에, 서블릿 컨텍스트가 시작될 때, 1개의 DispatcherServlet을 미리 시작합니다. 만약에, <load-on-startup>이 생략되거나 0이면, 최초의 요청이 들어올 때 DispatcherServlet을 로드합니다.
<url-pattern>정의에 의해, /example로 시작하는 모든 요청들은 example이라고 명시된 DispatcherServlet 인스턴스에 의해 관리됩니다. Servlet 3.0 이상부터는, xml이 아닌 프로그래밍으로도 설정할 수 있습니다.
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet());
registration.setLoadOnStartup(1);
registration.addMapping("/example/*");
}
}
* DispatcherServlet의 동작 방식
1. 사용자가 요청을 보내면, DispatercherServlet은 Handler Mapping과 Handler Adapter를 통해 요청에 해당하는 Controller로 요청을 보냅니다. @Controller, @RequestMapping 어노테이션을 사용합니다.
2. Controller에서 비지니스 로직이 처리되고, 그 결과 논리적 뷰(logical view)를 반환합니다. 혹은 REST 환경에서 @ResponseBody을 사용하여 HTTP 응답 바디로 결과물을 만들 수도 있습니다.
3. DispatcherServlet은 반환받은 논리적 뷰를 ViewResolver로 보내 해당하는 View를 찾습니다. ViewResolver는 체인으로 연결되어 있으며, JSP 같은 물리적 자원에 논리적인 뷰를 매핑합니다. ViewResolver는 순서대로 호출되는데, 해당하는 뷰가 없다면 null을 반환하고 다음 ViewResolver가 검사합니다.
4. DispatcherServlet은 View를 찾으면, JSP같은 렌더링을 위해서 Model 데이터와 함께 요청을 View로 보냅니다.
기본적으로 DispatcherServlet은 접두사, 접미사와 함께 InternalResourceViewResolver를 사용하여 논리적 뷰를, 예를들어, "home"을 /WEB-INF/home.jsp로 변환합니다. View 인터페이스는 또한 getContentType() 메서드를 이용하는데, 뷰가 생성하는 컨텐트 타입을 반환합니다.
5. 최종적으로 DispatcherServlet은 View 정보를 클라이언트에게 반환합니다.
* ContextLoaderListener
- ServletContextListener를 구현한 클래스로, WAS에서 서블릿 컨텍스트가 시작될 때, 스프링 관련 빈들을 Root WebApplicationContext에 적재하고 서블릿 컨텍스트가 종료될 때 빈들을 제거합니다. 즉, Spring의 Root WebApplicationContext의 시작과 종료를 담당하는 부트스트랩 리스너이며, 이미 생성된 Root ApplicationContext는 Servlet Context에 저장됩니다.
- 설정파일을 <context-param>의 contextConfigLocation 속성에서 설정하지 않으면 WEB-INF/applicationContext.xml 이 기본 설정파일 경로 및 이름이 됩니다.
- ContextLoaderListener와 DispatcherServlet은 각각 WebApplicationContext를 생성하는데, ContextLoaderListener가 생성한 컨텍스트가 Root WebApplicationContext가 됩니다. DispatcherServlet이 생성한 컨텍스트는 Root WebApplicationContext를 부모로 하는 자식 WebApplicationContext가 됩니다.(ContextLoadListener가 먼저 빈을 로드합니다.)
- 대부분 웹개발에서 자바 빈 설정은 여러 계층으로 나누어서 이루어지는데, 영속성 계층(Persistence Layer), 서비스 계층(Service Layer), 웹 단계(Web Tier) 등이 있습니다. DispatcherServlet 설정파일은 Web Tier 요소 즉, Controller, ViewResolver, LocaleResolver, MVC 기반 인프라와 관련된 빈들을 주로 정의합니다. 서비스 계층, 영속성 계층에 속하는 빈도 포함시킬 수 있으나, 별도로 관리하는 것이 좋습니다.
- ContextLoaderListener는 영속성 계층의 클래스(DAO, @Repository), 서비스 클래스(@Service), 엔티티(@Entity)와 같은 클래스들을 주로 로드합니다. 이러한 클래스들은 컨트롤러가 실행되기 전에 미리 메모리에 생성되어 있어야 하므로, 이를 위해 DispatcherServlet이 컨트롤러 클래스(@Controller)를 로드하기 전에 ContextLoaderListener를 통해 로드합니다.
- 프로젝트 설정 파일이 여러개라면, 모두 로드 되도록 하기 위해서 ContextLoader를 설정하며, ContextLoader는 DispatcherServlet이 로드하는 것 이외의 컨텍스트 파일 설정을 로드합니다.
- ContextLoaderListener는 설정파일의 위치를 알려주지 않으면 /WEB-INF/applicationContext.xml이라는 스프링 설정 파일을 찾습니다. 서블릿 컨텍스트에 contextConfigLocation이라는 파라미터를 설정하면 ContextLoader가 로드할 수 있는 하나 이상의 스프링 설정 파일을 지정할 수 있습니다.
- DispatcherServlet이 여러 개인 경우, 각각 별도의 자식 WebApplicationContext를 생성하고, 각 자식 컨텍스트가 독립적이므로 서로 자바 빈 공유가 불가능합니다. 이 경우 ContextLoaderListener를 이용하여 여러 DispatcherServlet의 설정파일을 한번에 로드하면 됩니다. 또한 여러 컨트롤러가 공유해야 하는 서비스 계층의 빈이 있다면 ContextLoaderListener를 이용하여 먼저 로드하게 됩니다. (자식 WebApplicationContext는 Root WebApplicationContext에 접근은 가능합니다.)
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
맨 위의 그림은 Root WebApplicationContext이 DispatcherServlet과 분리되어서 표현되어 있지만, 아래 사진처럼 스프링 공식문서에서는 Servlet WebApplicationConext와 Root WebApplicationContext가 모두 DispatcherServlet 안에 포함되어 있습니다. 각 WebApplicationContext가 생성되는 방식은 다르지만, 결국 요청을 처리하는 DispatcherServlet이 요청을 처리하는 과정에서 웹과 관련한 빈들 뿐 아니라, 서비스와 저장소 자바 빈들을 사용하기 때문에 같은 범주에 표현하지 않았나 생각합니다.
다음과 같이, contextConfigLocation에 <param-value></param-value> 가 비어있다면, DispatcherServlet에 1개의 Root WebApplicationContext만 존재할 수 있습니다.
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
* root WebApplicationContext
모든 Spring 웹앱에는 생명주기와 관련된 애플리케이션 컨텍스트인 root WebApplicationContext가 있습니다.
어플리케이션이 시작되면 이 컨텍스트가 시작합니다. 어플리케이션이 멈추면, 컨텍스트는 종료됩니다. 이 모든 게 servletContextListener 덕분에 가능합니다.
웹 어플리케이션에 있는 컨텍스트는 항상 WebApplicationContext의 인스턴스를 사용하며, ServletContext에 접근하기 위한 계약으로 ApplicationContext을 상속하는 구현체입니다.
응용 프로그램은 이 인터페이스의 구현 세부사항을 걱정하지 않아도 됩니다, root WebApplicationContext는 단순히 공유 빈들을 정의하는 중앙 집중 장소이기 때문입니다.
*web context
spring-web 모듈 중의 일부분인 ContextLoaderListener에 의해서 root WebApplicationContext가 관리됩니다.
기본적으로, 리스너는/WEB-INF/applicationContext.xml에서 XML 어플리케이션 컨텍스트를 확인하지만, XML 대신 자바 어노테이션을 사용해 변경 할 수도 있습니다.
따라서 web.xml 파일 같은 웹앱 기술서나 서블릿 환경에서 리스너를 설정할 수 있습니다.
* 출처
https://www.youtube.com/watch?v=kzXenekh0WY
https://www.java67.com/2017/06/what-is-use-of-dispatcherservlet-in-spring-mvc.html
https://www.baeldung.com/spring-web-contexts
https://docs.spring.io/spring-framework/docs/4.2.4.RELEASE/spring-framework-reference/html/mvc.html
'Spring > Spring MVC 5' 카테고리의 다른 글
생성자 vs setter vs field 의존성 주입 (0) | 2022.07.15 |
---|---|
미니프로젝트 (0) | 2020.05.29 |
Restful API (0) | 2020.05.28 |
MyBatis (0) | 2020.05.27 |
예외처리 (0) | 2020.05.27 |