Dispatcher Servlet에 대해서
#
DispatcherServlet
- Front Controller
- 클라이언트의 모든 요청을 받은 후 이를 처리할 핸들러에게 넘기고 핸들러가 처리한 결과를 받아 사용자에게 응답 결과를 보여준다.
- Dispatcher Servlet은 여러 컴포넌트를 이용해 작업을 처리한다.
Dispatcher Servlet의 과정
- 요청이 들어온다.
- 요청 선처리 작업
- Locale 결정(
org.springframework.web.servlet.LocaleResolver
)- Spring MVC는 지역화(똑같은 사이트를 들어가도 영어로 화면처리를 하거나 한국어로 화면 처리를 하는 것)를 지원한다
- 브라우저가 보내는 헤더 정보에서 언어 설정 값을 이용해서 지역화 즉, Locale을 결정할 수 있다.
- RequestContextHolder에 요청을 저장(
org.springframework.web.context.request.RequestContextHolder
)- 스레드 로컬 객체로 요청을 받아서 응답할 때까지 HttpServletRequest, HttpServletResponse 등을 Spring이 관리하는 객체 안에서 사용할 수 있게 해준다.
- 컨트롤러 안에서 Request 객체가 필요할 때 그냥
HttpServletRequest request
만 선언하면 되듯이.
- FlashMap 복원(
org.springframework.web.servlet.FlashMapManager
)- redirect로 값을 전달할 때 ? 파라미터를 이용하는데 값을 딱 한 번 유지시킬 수 있게 해주는 것을 이야기 한다.
- 리다이렉트 후 조회를 하면 정보는 바로 삭제된다.
- Multipart 요청(
org.springframework.web.multipart.MultipartResolver
)- 사용자가 파일 업로드를 했을 때 특수한 형태의 Request 객체가 필요하다. 멀티 파트 요청이 들어오면 Request가 MultipartResolver가 멀티파트를 결정할 수 있도록 해준다.
- Locale 결정(
- 요청 전달
- HandlerMapping으로 HandlerExecutionChain 결정
- HandlerMapping(
org.springframework.web.servlet.HandlerMapping
)은 어떤 핸들러가 요청을 처리할지에 대한 정보를 알고 있다. - HandlerExecutionChain(
org.springframework.web.servlet.HandlerExecutionChain
)은 실제로 호출된 핸들러에 대한 참조를 가지고 있다. 무엇이 실행되어야 할지 알고 있는 객체 - HandlerExecutionChain을 발견하지 못 했으면 페이지가 없는 것으로 404 전달
- HandlerMapping(
- HandlerExecutionChain 실행
- HandlerExecutionChain를 발견했다면 HandlerAdapter를 결정
- 만약 HandlerAdapter가 없다면 ServletException
- HandlerAdapter(
org.springframework.web.servlet.HandlerAdapter
)는 실제 핸들러를 실행하는 역할을 담당한다.@RequestMapping
과@Controller
어노테이션을 통해 정의되는 컨트롤러의 경우 DefaultAnnotationHandlerMapping에 의해 핸들러가 결정되고 AnnotationMethodHadnlerAdapter에 의해 호출이 일어난다. - 사용가능한 인터셉터(필터역할)이 존재한다면 인터셉터의 preHandle을 호출해 요청을 처리한다.
- 그리고나서 핸들러를 실행한다.
- 핸들러가 ModelAndView를 리턴하는지(
org.springframework.web.servlet.ModelAndView
), ModelAndView가 뷰를 갖는지에 따라(org.springframework.web.servlet.RequestToViewNameTranslator
, 컨트롤러에서 뷰를 제공하지 않았을 경우 URL과 같은 요청정보를 참고해서 자동으로 뷰 이름을 생성해준다.) 동작하는 게 달라진다. 결국에는 인터셉터의 preHandle을 호출해서 요청을 처리한다.
- 핸들러가 ModelAndView를 리턴하는지(
- HandlerMapping으로 HandlerExecutionChain 결정
- 예외 처리
- 예외가 발생하면 HandlerExceptionResolver(
org.springframework.web.servlet.handlerexceptionresolver
)에 문의한다. - HandlerExceptionResolver는 예외가 던져졌을 때 어떤 핸들러를 실행할 것인지 알려준다.
- 예외가 발생하면 HandlerExceptionResolver(
- 뷰 렌더링
- ViewResolver를 이용해서 컨트롤러가 리턴한 뷰 이름을 참고해서 적절한 뷰 오브젝트를 찾아주는 로직을 가진 오브젝트다.
- 요청 처리 종료
Dispatcher Servlet을 FrontController로 설정하기
-
3가지 방법이 있다.
-
DispatcherServlet이 FrontController의 역할을 한다는 설정을 해주기 위한 3가지 방법이 있다
- web.xml에 설정(여기서도 두 가지 방법으로 나눠진다)
- WebMVCContextConfig.xml xml파일 자체를 web.xml에 추가하는 방법
- web.xml에 설정(여기서도 두 가지 방법으로 나눠진다)
<web-app>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:WebMVCContextConfig.xml</param-value>
</init-param>
</servlet>
</web-app>
* java config 클래스를 읽어들이도록 web.xml에 추가하는 방법
* `<servlet-mapping>`를 보면 url-pattern에 `/`가 있다. 이 뜻는 모든 요청을 받는 다는 뜻이다.
* 모든 요청을 받아서 servlet-name으로 설정되어 있는 servlet으로 맵핑된다. 이 서블릿을 frontController로 한다고 말해주는 것
* param-value에 보면 java config파일을 가져오는 걸 확인할 수 있다.
* 클래스 명을 줄 때 패키지 명을 포함한 클래스 명을 줘야 한다.
* contextclass는 bean 공장을 뜻한다. ```xml
- dispatcher servlet의 java config 파일(WebMvcContextConfiguration.java)
```java
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"kr.or.connect.mvcexam.controller"})
public class WebMvcContextConfiguration extends WebMvcConfigurerAdapter {
// css나 img나 js같은 요청들이 들어왔을 때 바로 컨트롤러로 가는 게 아니라 여기를 거치게 만들고 여기서 찾으라고 만들어 놓은 것.
// 옛날에는 web.xml에서 경로를 하나하나 다르게 줘서 구별했었다.(.do라든지...)
// 이게 없다면 다 controller에 가서 찾게 된다.
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/META-INF/resources/webjars/").setCachePeriod(31556926);
registry.addResourceHandler("/css/**").addResourceLocations("/css/").setCachePeriod(31556926);
registry.addResourceHandler("/img/**").addResourceLocations("/img/").setCachePeriod(31556926);
registry.addResourceHandler("/js/**").addResourceLocations("/js/").setCachePeriod(31556926);
}
// 매핑 정보가 없는 URL 요청은 Spring의 DefaultServletHttpRequestHandler가 처리하도록 해준다.
// Spring의 DefaultServletHttpRequestHandler은 WAS의 DefaultServlet에게 해당 일을 넘기게 된다. 그러면 WAS는 DefaultServlet이 static한 자원을 읽어서 보여주게 한다.
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
// 특정 URL에 대한 처리를 controller 클래스를 작성하지 않고 매핑할 수 있도록 해준다.
// 요청 자체가 "/"로 들어오면 main이라는 이름의 view로 보여주게 하는 것
// view name은 아래 함수인 viewResolver라는 객체를 이용해서 찾는다.
@Override
public void addViewControllers(final ViewControllerRegistry registry) {
System.out.println("addViewControllers가 호출됩니다. ");
registry.addViewController("/").setViewName("main");
}
// 여기에 view 정보 주고 있다.
// view는 WEB-INF/views/main.jsp라는 파일을 찾아주는 것.
@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
// 이러한 설정들을 읽어서 DispatcherServlet이 동작하게 된다.
}
* javax.servlet.ServletContainerInitializer 사용(servlet 3.0 이상에서 사용)(여기서 설명 X)
* org.springframework.web.WebApplicationInitializer 인터페이스를 구현해서 사용(여기서 설명 X)
* 좀 느릴 수 있다.