DispatcherServlet
서블릿
클라이언트 요청을 처리하고, 그 결과를 반환하는 웹 프로그래밍 기술이다
j2EE (Java EE)
자바 기술로 어플을 만들 때 필요한 스펙(구성요소, api, 런타임 컨테이너, 서비스 등에 대한 표준 사양)들의 집합이다. 이전에는 J2EE라 불리었으나 버전 5.0 이후로 Java EE로 개칭되었다.
JSP(Java Server Page), Java Servlet, EJB(Enterprise JavaBeans) 등의 구성요소로 구성된다. 개발자들은 이런 구성요소들로 대규모 분산 응용 프로그램을 구축할 수도 있다.
우리는 이런 Java EE 응용 프로그램을 JAR 파일로 변환해 인스턴스로 배포하게 된다.
Dispatcher Servlet
Servlet의 일종
Dispatcher의 뜻은 가장 먼저 요청을 먼저 받고, 적절하게 처리할 함수, 즉 컨트롤러를 찾아서 정해주는 역할을 한다.
public class DispatcherServlet extends FrameworkServlet {}
public abstract class FrameworkServlet extends HttpServletBean {}
public abstract class HttpServletBean extends HttpServlet {}
디스패터 서블릿은 HttpServlet 을 상속한다.
DispatcherServlet → FrameworkServlet → HttpServletBean → HttpServlet
프론트 컨트롤러 패턴 (Front Controller)
과거에는 모든 서블릿을 URL 매핑을 위해 web.xml에 등록해야 했지만, 이젠 디스패처 서블릿이 있다
디스패처 서블릿은 스프링 MVC의 중앙 서블릿이며 어플리케이션으로 오는 모든 요청을 핸들링하고 공통작업을 처리해준다.
디스패처 서블릿을 프론트 컨트롤러 라고도 부른다. 프론트 컨트롤러는 서블릿 컨테이너 맨 앞에서 모든 요청을 받아 처리해주는 컨트롤러를 의미한다.
설정 방법
등록 및 초기화
디스패쳐 서블릿도 자바나 web.xml을 통해 등록 및 초기화 되어야 사용 가능하다.
톰캣 등 서블릿 컨테이너를 통해 디스패처 서블릿의 생명주기를 관리하는 것이 일반적이다. 아래 방법으로 원시적인 설정도 가능하다.
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AppConfig.class);
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(context);
ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
Java
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>app</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>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
Servlet Container
서블릿은 스스로 동작하지 않는다. 보통 서블릿을 관리할 서블릿 컨테이너(톰캣 등)를 사용한다.
서블릿 컨테이너는 싱글톤인 서블릿 객체의 생명주기를 관리한다. 또한 웹서버와 소켓으로 통신하며, 클라이언트의 요청을 받고 응답을 보내준다. 멀티쓰레드 처리도 지원한다.
WebApplicationContext
DispatcherServlet 동작 방식
- Servlet WebApplicationContext을 생성하고
- 보통 Root WebApplicationContext 을 생성한다.
서블릿 WebApplicationContext 안에는 ServletContext와 연관된 Servlet 링크들이 있다.
Controller, ViewResolver, HandlerMapping 등이 이 안에 있다. RequestContextUtils 의 정적 메소드들을 사용해 어플리케이션이 WebApplicationContext를 조회할 수 있다.
Root WebApplicationContext 가 있는 경우, infra bean이 이 안에 들어간다. Repository, Service 빈 등이 인프라 빈이다. WebApplicationContext 설정은 java나 web.xml 을 통해 할 수 있다.
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { App1Config.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/app1/*" };
}
}
Java
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>app1</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app1-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app1</servlet-name>
<url-pattern>/app1/*</url-pattern>
</servlet-mapping>
</web-app>
DispatcherServlet 동작 방식
- 클라이언트(아마 프론트 서버) 요청을 디스패처 서블릿이 받는다.
- (이때 filter를 경유할 수 있다)
- 요청 정보를 보고 Servlet WebApplicationContext 안에서 HandlerMapping을 통해 요청을 위임할 컨트롤러를 검색해 찾는다.
- 찾은 컨트롤러로 요청을 위임할 HandlerAdapter를 찾는다. 이때 Interceptor preHandle이 실행된다.
- HandlerAdapter가 컨트롤러로 요청(HttpRequest)을 위임한다 Argument Resolver가 파라미터를 처리해 컨트롤러로 보낸다.
- 컨트롤러는 Root WebApplicationContext 속 Service, Repository… 등을 호출해가며 비즈니스 로직을 처리한다.
- 컨트롤러 메소드에서 최종적인 반환값(Response Entity)이 리턴된다.
- 리턴 값에 대해 Interceptor postHandle이 실행된다.
- 핸들러 어댑터 리턴값을 ViewResolver에 전달한다.
- ViewResolver가 View를 검색하고 찾은 View에 응답을 전달한다.
- 이후 Interceptor afterCompletion이 실행된다.
- 디스패처 서블릿에서 View로부터 받은 응답을 클라이언트(프론트 서버)로 반환해준다!