(스프링) 스프링 MVC 프레임워크 동작방식
*src/main/webapp/WEB-INF/web.xml 파일에 DispatcherServlet를 설정한다.
DispatcherServlet은 전달받은 설정 파일을 이용해서 스프링 컨테이너를 생성하는데, HandlerMapping, HandlerAdapter, 컨트롤러, ViewResolver등의 빈은 DispatcherServlet이 생성하는 스프링 컨테이너로부터 구한다.
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet- context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
---->contextConfiguration 초기화 파라미터를 설정하지 않은 경우, DispatcherServlet은
WEB-INF/[서블릿이름]-servlet.ml 파일을 설정파일로 사용한다.
여기서는 다음과 같다.
/WEB-INF/spring/appServlet/servlet-context.xml
servlet-context.xml에
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
컨트롤러의 실행 결과를 받은 DispatcherServlet은 ViewResolver에게 뷰 이름에 해당하는 View객체를 요청하는데, 이떄 InternalResourceViewResolver는 "prefix+뷰이름+suffix"에 해당하는 경로를 뷰 코드로 사용하는 InternalResourceView타입의 VIew 객체를 리턴한다.
DispatcherServlet은 컨트롤러의 실행 결과를 HandlerAdapter를 통해서 ModelAndView 형태로 전달받는다고 했는데, 이 중에서 Model에 해당하는 값을 View객체에 Map형식으로 전달한다. 예를들어 HelloController클래스는 다음과 같이 Model에 "greeting"속성을 설정했었다.
model.addAttribute("greeting","안녕하세요"+name);
이 경우 DispatcherServlet은 View객체에 응답 생성을 요청할 때, greeting키를 갖는데 Map객체를 View객체에 전달한다.
<body>
인사말: ${greeting}
</body>
DispatcherServlet은 전달받은 설정 파일을 이용해서 스프링 컨테이너를 생성하는데, HandlerMapping, HandlerAdapter, 컨트롤러, ViewResolver등의 빈은 DispatcherServlet이 생성하는 스프링 컨테이너로부터 구한다.
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet- context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
---->contextConfiguration 초기화 파라미터를 설정하지 않은 경우, DispatcherServlet은
WEB-INF/[서블릿이름]-servlet.ml 파일을 설정파일로 사용한다.
여기서는 다음과 같다.
/WEB-INF/spring/appServlet/servlet-context.xml
* @Controller를 위한 HandlerMapping 과 HandlerAdapter
DispatcherServlet은 스프링 컨테이너에서 HandlerMapping과 HandlerAdapter타입의 빈을 사용하므로, 사용하려는 핸들러에 알맞은 HandlerMapping 빈과 HandlerAdapter빈이 스프링 설정에 등록되어 있어야 한다.
servlet-context.xml에 있는 <annotation-driven/>은 매우 다양한 스프링 빈 설정을 추가해준다.
이 태그가 빈으로 추가해주는 클래스 중에는 @Controller타입의 핸들러 객체를 처리하기 위한 다음의 두 클래스도 포함되어 있다.
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
RequestMappingHandlerMapping 애노테이션은 @Controller애노테이션이 적용된 객체의 @RequestMapping값을 이용해서 웹 브라우저의 요청을 처리할 컨트롤러 빈을 찾는다.
RequestMappingHandlerAdapter애노테이션은 컨트롤러의 메소드를 알맞게 실행하고, 그 결과를 ModelAndView 객체로 변환해서 DispatcherServlet에 리턴한다.
RequestMappingHandlerAdapter 클래스는 "/hello" 요청 경로에 대해 hello()메소드를 호출하는데, 이때 Model객체를 생성해서 첫 번쨰 파라미터로 전달한다.
*JSP를 위한 ViewResolver
컨트롤러의 처리결과를 JSP를 이용해서 생성하려면 ,다음의 설정을 추가해주면된다.servlet-context.xml에
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
컨트롤러의 실행 결과를 받은 DispatcherServlet은 ViewResolver에게 뷰 이름에 해당하는 View객체를 요청하는데, 이떄 InternalResourceViewResolver는 "prefix+뷰이름+suffix"에 해당하는 경로를 뷰 코드로 사용하는 InternalResourceView타입의 VIew 객체를 리턴한다.
DispatcherServlet은 컨트롤러의 실행 결과를 HandlerAdapter를 통해서 ModelAndView 형태로 전달받는다고 했는데, 이 중에서 Model에 해당하는 값을 View객체에 Map형식으로 전달한다. 예를들어 HelloController클래스는 다음과 같이 Model에 "greeting"속성을 설정했었다.
model.addAttribute("greeting","안녕하세요"+name);
이 경우 DispatcherServlet은 View객체에 응답 생성을 요청할 때, greeting키를 갖는데 Map객체를 View객체에 전달한다.
<body>
인사말: ${greeting}
</body>
* 디폴트 핸들러와 HandlerMapping의 우선순위
web.xml 설정을 보면 DispatcherServlet에 대한 매핑 경로를 다음과 같이 '/'로 주었다.
매핑경로가 '/'인 경우, .jsp로 끝나는 요청을 제외한 모든 요청을 DispatcherServlet이 처리하게 된다.
즉, /index.html 이나 /css/bootstrap.css와 같은 요청을 DispatcherServlet이 처리하게 된다.
그런데 <annotation-driven>태그를 통해서 등록되는 HandlerMapping은 @Controller애노테이션이 적용된 빈 객체가 처리할 수 있는 요청 경로만 대응할 수 있다.
예를들어, 등록된 컨트롤러가 한 개 이고 그 컨트롤러가 @RequestMapping("/hello")를 설정하고 있다면, /hello 경로만 처리할 수 있게 된다.
따라서 /index.html 이나 css/bootstrap.css와 같은 요청을 처리할 수 있는 컨트롤러 객체를 찾지못해 DispatcherServlet은 404 응답을 전송하게된다.
/index.html 이나 /css/bootstrap.css와 같은 경로를 처리하기 위한 컨트롤러 객체를 직접 구현해 줄 수도있겠지만, 그것보다는 <default-servlet-handler>설정을 사용하는 것이 좋다.
<default-servlet-handler>태그는 다음의 두 빈 객체를 추가해 준다.
- DefaultServletHttpRequestHandler
- SimpleUrlHandlerMapping
DefaultServletHttpRequestHandler는 클라이언트의 모든 요청을 WAS(웹 어플레케이션 서버, 톰캣이나 웹로직 등)가 제공하는 기본 서블릿에 전달한다.
예를들어, "/index.html"에 대한 처리를 DefaultServletHttpRequestHandler에 요청하면 이 요청을 다시 기본 서블릿에 전달해서 처리하도록 한다. 그리고 SimpleUrlHandlerMapping을 이용해서 모든 경로("/**")를 DefaultServletHttpRequestHandler를 이용해서 처리하도록 설정한다.
<annotationn-drive>태그가 등록하는 RequestMappingHandlerMapping의 적용 우선순위가 <default-servlet-handler>가 등록하는 SimpleUrlHandlerMapping의 우선순위보다 높기 떄문에, 웹 브라우저의 요청이 들어오면 DispatcherServlet은 다음과 같은 방식으로 요청을 처리한다.
- RequestMappingHandlerMapping을 사용해서 요청을 처리할 핸들러를 검색한다. 존재하면, 해당 컨트롤러를 이용해서 요청을 처리한다.
- 존재하지 않으면, SimplUrlHandlerMapping을 사용해서 요청을 처리할 핸들러를 검색한다. <default-servlet-handler>가 등록한 SimplUrlHandlerMapping은 "/**"경로에 대해 DefaultServletHttpRequestHandler를 리턴한다. DispatcherServlet은 DefaultServletHttpRequestHandler에 처리를 요청한다. DefaultServletHttpRequestHandler는 디폴트 서블릿에 처리를 위임한다.
블로그 관리자가 댓글을 삭제했습니다.
답글삭제