spring mvc Dispatcher Servlet 의 전단 컨트롤 러 구조 상세 설명

전단 컨트롤 러 는 전체 MVC 프레임 워 크 에서 가장 핵심 적 인 부분 으로 요구 에 부 합 된 외부 요청 을 차단 하고 요청 을 서로 다른 컨트롤 러 에 나 누 어 처리 하 며 컨트롤 러 처리 후의 결과 에 따라 해당 하 는 응답 을 생 성하 여 클 라 이언 트 에 보 냅 니 다.전단 컨트롤 러 는 Filter 를 사용 하여(Struts 2 는 이런 방식 으로)구현 할 수도 있 고,Servlet 을 사용 하여 실현 할 수도 있다(spring MVC 프레임 워 크).

Dispatcher Servlet 는 사전 컨트롤 러 로 서 웹 서버 의 입구 이 며,spring mvc 의 가장 중요 한 유형 으로 수명 주 기 를 통 해 웹 서버 에 대한 이 해 를 강화 할 수 있 습 니 다.
servlet 의 생명주기
우선 servlet 의 생명 주 기 를 회상 해 봅 시다.
Servlet 의 생명 주 기 는 세 단계 로 나 뉜 다.[Servlet 생명주기 와 작업 원리 에 대한 상세 한 설명]
1.초기 화 단계  init()방법 을 호출 합 니 다.Servlet 이 불 러 온 후 Servlet 용 기 는 Servlet 인 스 턴 스 를 만 들 고 Servlet 의 init()방법 으로 초기 화 합 니 다.Servlet 의 전체 수명 주기 내 에 init()방법 은 한 번 만 호출 됩 니 다.
2.고객 요청 단계 호출 서비스()방법 에 응답
3.종료 단계 destroy()방법 호출
Servlet 초기 화 단계
다음 시간 에 Servlet 용기 에 Servlet 를 불 러 옵 니 다:
1.Servlet 용기 가 시 작 될 때 일부 Servlet 을 자동 으로 불 러 옵 니 다.웹.XML 파일 의사이 에 다음 코드 만 추가 하면 됩 니 다.
1
2.Servlet 용기 가 시 작 된 후 클 라 이언 트 가 처음으로 Servlet 에 요청 을 보 냅 니 다.
3.Servlet 클래스 파일 이 업데이트 되면 Servlet 를 다시 불 러 옵 니 다.
Dispatcher Servlet 의 구조
상기 지식 을 복습 한 후에 디 스 패 치 서버 의 구 조 를 살 펴 보 겠 습 니 다.
Dispatcher Servlet 은 추상 클래스 에서 계승 합 니 다:Framework Servlet 은 HttpServlet(Framework Servlet 은 HttpServletBean 에서 계승 하고 HttpServletBean 은 HttpServlet 에서 계승 합 니 다)
Servlet 초기 화

 protected void initStrategies(ApplicationContext context) {
 initMultipartResolver(context); //      ,       multipart   MultipartResolver        ;
 initLocaleResolver(context); //     
 initThemeResolver(context);   //    
 initHandlerMappings(context); //  HandlerMapping,         
 initHandlerAdapters(context); //  HandlerAdapter          
 initHandlerExceptionResolvers(context); //              HandlerExceptionResolver   
 initRequestToViewNameTranslator(context); //          
 initViewResolvers(context); //  ViewResolver              
 initFlashMapManager(context); //flash     
 }
servlet 요청 처리 방법:
servlet 의 service 방법 으로 http 요청 을 처리 합 니 다.
Framework Servlet.java 는 servlet 의 service 와 destroy 방법 을 정의 합 니 다.다음 과 같 습 니 다.

 /**
 * Override the parent class implementation in order to intercept PATCH
 * requests.
 */
 @Override
 protected void service(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {

 String method = request.getMethod();
 if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
  processRequest(request, response);
 }
 else {
  super.service(request, response);
 }
 }

http 요청 형식 은 7 가지(옵션 옵션 추가)가 있 음 을 알 고 있 습 니 다.정 의 는 다음 과 같 습 니 다.

public enum RequestMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE

}

Framework Servlet 의 service()는 서로 다른 요청 을 처리 합 니 다.우 리 는 흔히 볼 수 있 는 post 로 설명 합 니 다.

 /**
 * Process this request, publishing an event regardless of the outcome.
 * <p>The actual event handling is performed by the abstract
 * {@link #doService} template method.
 */
 protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {

 long startTime = System.currentTimeMillis();
 Throwable failureCause = null;

 LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
 LocaleContext localeContext = buildLocaleContext(request);

 RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
 ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
 asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

 initContextHolders(request, localeContext, requestAttributes);

 try {
  doService(request, response);
 }
 catch (ServletException ex) {
  failureCause = ex;
  throw ex;
 }
 catch (IOException ex) {
  failureCause = ex;
  throw ex;
 }
 catch (Throwable ex) {
  failureCause = ex;
  throw new NestedServletException("Request processing failed", ex);
 }

 finally {
  resetContextHolders(request, previousLocaleContext, previousAttributes);
  if (requestAttributes != null) {
  requestAttributes.requestCompleted();
  }

  if (logger.isDebugEnabled()) {
  if (failureCause != null) {
   this.logger.debug("Could not complete request", failureCause);
  }
  else {
   if (asyncManager.isConcurrentHandlingStarted()) {
   logger.debug("Leaving response open for concurrent processing");
   }
   else {
   this.logger.debug("Successfully completed request");
   }
  }
  }

  publishRequestHandledEvent(request, startTime, failureCause);
 }
 }

Framework Servlet 는 처리 절 차 를 추상 적 으로 정의 하고 하위 클래스 를 남 겨 두 어 이 방법 을 실현 하 며 구체 적 인 요청 처 리 를 완성 합 니 다.

/**
 * Subclasses must implement this method to do the work of request handling,
 * receiving a centralized callback for GET, POST, PUT and DELETE.
 * <p>The contract is essentially the same as that for the commonly overridden
 * {@code doGet} or {@code doPost} methods of HttpServlet.
 * <p>This class intercepts calls to ensure that exception handling and
 * event publication takes place.
 * @param request current HTTP request
 * @param response current HTTP response
 * @throws Exception in case of any kind of processing failure
 * @see javax.servlet.http.HttpServlet#doGet
 * @see javax.servlet.http.HttpServlet#doPost
 */
 protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
  throws Exception;
구체 적 인 실현 은 다음 과 같다.

 /**
 * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
 * for the actual dispatching.
 */
 @Override
 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
 if (logger.isDebugEnabled()) {
  String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
  logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
   " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
 }

 // Keep a snapshot of the request attributes in case of an include,
 // to be able to restore the original attributes after the include.
 Map<String, Object> attributesSnapshot = null;
 if (WebUtils.isIncludeRequest(request)) {
  attributesSnapshot = new HashMap<String, Object>();
  Enumeration<?> attrNames = request.getAttributeNames();
  while (attrNames.hasMoreElements()) {
  String attrName = (String) attrNames.nextElement();
  if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
   attributesSnapshot.put(attrName, request.getAttribute(attrName));
  }
  }
 }

 // Make framework objects available to handlers and view objects.
 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
 request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
 request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
 request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

 FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
 if (inputFlashMap != null) {
  request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
 }
 request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
 request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

 try {
  doDispatch(request, response);
 }
 finally {
  if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
  return;
  }
  // Restore the original attribute snapshot, in case of an include.
  if (attributesSnapshot != null) {
  restoreAttributesAfterInclude(request, attributesSnapshot);
  }
 }
 }

중요 한 장면 은 분배 기의 실현 을 요청 하 는 것 이다.
기능:1.handler 에 요청 을 전달 합 니 다(설정 순서에 따라 servlet 의 맵 관 계 를 가 져 와 handler 를 가 져 옵 니 다).2.servlet 에 설 치 된  Handler Adapters 는 첫 번 째 처리 할 수 있 는 handler 를 조회 합 니 다.3.handler 자극 처리 요청

 /**
 * Process the actual dispatching to the handler.
 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
 * to find the first that supports the handler class.
 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
 * themselves to decide which methods are acceptable.
 * @param request current HTTP request
 * @param response current HTTP response
 * @throws Exception in case of any kind of processing failure
 */
 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
 HttpServletRequest processedRequest = request;
 HandlerExecutionChain mappedHandler = null;
 boolean multipartRequestParsed = false;

 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

 try {
  ModelAndView mv = null;
  Exception dispatchException = null;

  try {
  processedRequest = checkMultipart(request);
  multipartRequestParsed = (processedRequest != request);

  // Determine handler for the current request.
  mappedHandler = getHandler(processedRequest);
  if (mappedHandler == null || mappedHandler.getHandler() == null) {
   noHandlerFound(processedRequest, response);
   return;
  }

  // Determine handler adapter for the current request.
  HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

  // Process last-modified header, if supported by the handler.
  String method = request.getMethod();
  boolean isGet = "GET".equals(method);
  if (isGet || "HEAD".equals(method)) {
   long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
   if (logger.isDebugEnabled()) {
   logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
   }
   if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
   return;
   }
  }

  if (!mappedHandler.applyPreHandle(processedRequest, response)) {
   return;
  }

  try {
   // Actually invoke the handler.
   mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  }
  finally {
   if (asyncManager.isConcurrentHandlingStarted()) {
   return;
   }
  }

  applyDefaultViewName(request, mv);
  mappedHandler.applyPostHandle(processedRequest, response, mv);
  }
  catch (Exception ex) {
  dispatchException = ex;
  }
  processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
 }
 catch (Exception ex) {
  triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
 }
 catch (Error err) {
  triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
 }
 finally {
  if (asyncManager.isConcurrentHandlingStarted()) {
  // Instead of postHandle and afterCompletion
  mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
  return;
  }
  // Clean up any resources used by a multipart request.
  if (multipartRequestParsed) {
  cleanupMultipart(processedRequest);
  }
 }
 }

servlet 소각

 /**
 * Close the WebApplicationContext of this servlet.
 * @see org.springframework.context.ConfigurableApplicationContext#close()
 */
 @Override
 public void destroy() {
 getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
 // Only call close() on WebApplicationContext if locally managed...
 if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) {
  ((ConfigurableApplicationContext) this.webApplicationContext).close();
 }
 }


소결:
본 고 는 장 제한 으로 처리 요청 절차 만 소개 하고 코드 에 대해 깊이 분석 하지 않 았 으 며 다음 글 은 작은 부분 에서 시작 하여 spring 의 코드 미 를 분석 할 것 이다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기