Spring MVCのDispatcherServiceリクエスト処理

15416 ワード

処理要求はMVCのC(Control)部分であり、MVCの核心内容である.初期化コンテキストでは、すべてのHandlerMappingをhandlerMappingsにロードし、Orderに従ってソートします.各HandlerMappingは、ControllerへのURLのマッピング関係を持つ.
MVC初期化完了後、Http要求の処理はdoService()メソッドで行われる.DispatcherServiceletはHttptServiceletのサブクラスであり、Httpリクエストの処理はdoService()メソッドで行われる.
初期化が完了すると、コンテキストで構成されたすべてのHandlerMappingがロードされた(HandlerMap初期化)、並べ替えられて1つのリストに格納される.HandlerMappingは、Http要求と対応するプロセッサのマッピングデータとを格納する.
   
/**
 *  Http   handler          *              ,        ,         BeanNameUrlHanlderMapping SimpleUrlHandlerMapping。 *         HandlerMapping    HandlerMapping    HandlerMapping
 * *HandlerMapping           ,      。  Handler     HandlerExutionChain   ,          *  。DispatcherServlet              preHandler  ,         preHandler     true,    ** handler  。
 **/
 public interface HandlerMapping {

	
	String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";

	
	String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";

	
	String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";

	
	String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";

	
	String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

	/**
	                  。          URL,             。
	    HandlerExecutionChain         ,         ,            
	                null,       。
	   DispatcherServlet         HanlderMapping      Handler,                 
	 */
	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

}

 
要求の実際の処理はdoDispatche()メソッドで行われ、主なステップは次のとおりです.
 
  • ModelAndView,
  • の準備
  • getHandlerを呼び出して、要求を処理するHandlerを取得し、
  • .
  • そしてhandler応答要求によりModelAndViewオブジェクトを返す
  • 最後に、このModelAndViewをビューオブジェクトに渡して表示します.

  •  
     
    doServiceはHttpService Request要求を受信し、要求パラメータの初期化後にdoDispatch()に直接要求を委任します.線面はdoServiceソースです.
     
    /**
    	 * 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 requestUri = urlPathHelper.getRequestUri(request);
    			logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +
    					" request for [" + requestUri + "]");
    		}
    
    		// 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)) {
    			logger.debug("Taking snapshot of request attributes before include");
    			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 {
    			// Restore the original attribute snapshot, in case of an include.
    			if (attributesSnapshot != null) {
    				restoreAttributesAfterInclude(request, attributesSnapshot);
    			}
    		}
    	}
    

     
    doDispatch()は、HttpServertRequestリクエストが真に処理されるクラスであり、
    このクラスではまずHandlerMappingsから順にマッチングの有無をチェックする処理方法である.HandlerMappings内のすべてのhandlerMapが一致する処理方法がない場合は、404を返す.次はdoDispatchソースコードの分析です.
     
    /**
    	 * 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;
    		int interceptorIndex = -1;
    
    		try {
    			ModelAndView mv;
    			boolean errorView = false;
    
    			try {
    				//             
    				processedRequest = checkMultipart(request);
    
    				//           HandlerExecutionChain
    				mappedHandler = getHandler(processedRequest, false);
    				if (mappedHandler == null || mappedHandler.getHandler() == null) {
    				//       ,     404
    					noHandlerFound(processedRequest, response);
    					return;
    				}
    
    				//        Handler   
    				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
                    // Process last-modified header, if supported by the handler.
    				//           
    				String method = request.getMethod();
    				//            GET  
    				boolean isGet = "GET".equals(method);
    				//    GET     HEAD
    				if (isGet || "HEAD".equals(method)) {
    					//        
    					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
    					if (logger.isDebugEnabled()) {
    						String requestUri = urlPathHelper.getRequestUri(request);
    						logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
    					}
    					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    						return;
    					}
    				}
    
    				//      
    				HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
    				//        
    				if (interceptors != null) {
    					for (int i = 0; i < interceptors.length; i++) {
    						HandlerInterceptor interceptor = interceptors[i];
    						//         ,     ,    
    						if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
    							triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
    							return;
    						}
    						interceptorIndex = i;
    					}
    				}
    
    				//            
    				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    				//       ,  view,         
    				if (mv != null && !mv.hasView()) {
    					mv.setViewName(getDefaultViewName(request));
    				}
    
    				// Apply postHandle methods of registered interceptors.
    				if (interceptors != null) {
    					for (int i = interceptors.length - 1; i >= 0; i--) {
    						HandlerInterceptor interceptor = interceptors[i];
    						interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
    					}
    				}
    			}
    			catch (ModelAndViewDefiningException ex) {
    				logger.debug("ModelAndViewDefiningException encountered", ex);
    				mv = ex.getModelAndView();
    			}
    			catch (Exception ex) {
    				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
    				mv = processHandlerException(processedRequest, response, handler, ex);
    				errorView = (mv != null);
    			}
    
    			//     
    			if (mv != null && !mv.wasCleared()) {
    				render(mv, processedRequest, response);
    				if (errorView) {
    					WebUtils.clearErrorRequestAttributes(request);
    				}
    			}
    			else {
    				if (logger.isDebugEnabled()) {
    					logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
    							"': assuming HandlerAdapter completed request handling");
    				}
    			}
    
    			// Trigger after-completion for successful outcome.
    			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
    		}
    
    		catch (Exception ex) {
    			// Trigger after-completion for thrown exception.
    			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
    			throw ex;
    		}
    		catch (Error err) {
    			ServletException ex = new NestedServletException("Handler processing failed", err);
    			// Trigger after-completion for thrown exception.
    			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
    			throw ex;
    		}
    
    		finally {
    			// Clean up any resources used by a multipart request.
    			if (processedRequest != request) {
    				cleanupMultipart(processedRequest);
    			}
    		}
    	}

    getHandlerはhandlerMappingsを遍歴するプロセスであり、そのうちの1つのhandlerMappingsに一致する処理方法があればそのHandlerExecutionChainに戻る.
    	/**
    	 * Return the HandlerExecutionChain for this request.
    	 * <p>Tries all handler mappings in order.
    	 * @param request current HTTP request
    	 * @return the HandlerExecutionChain, or <code>null</code> if no handler could be found
    	 */
    	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    		for (HandlerMapping hm : this.handlerMappings) {
    			if (logger.isTraceEnabled()) {
    				logger.trace(
    						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
    			}
    			HandlerExecutionChain handler = hm.getHandler(request);
    			if (handler != null) {
    				return handler;
    			}
    		}
    		return null;
    	}

     
    	/**
    	 *             ,                      
    	 * @param request current HTTP request
    	 * @return the corresponding handler instance, or the default handler
    	 * @see #getHandlerInternal
    	 */
    	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    		Object handler = getHandlerInternal(request);
    		//                
    		if (handler == null) {
    			handler = getDefaultHandler();
    		}
    		//              null
    		if (handler == null) {
    			return null;
    		}
    		//      Bean   
    		if (handler instanceof String) {
    			String handlerName = (String) handler;
    			//           
    			handler = getApplicationContext().getBean(handlerName);
    		}
    		return getHandlerExecutionChain(handler, request);
    	}

     
    HandlerExeutionChainにはHnadlerオブジェクトといくつかの列のブロッキングが含まれています
    /**
     * Handler execution chain, consisting of handler object and any handler interceptors.
     * Returned by HandlerMapping's {@link HandlerMapping#getHandler} method.
     *
     * @author Juergen Hoeller
     * @since 20.06.2003
     * @see HandlerInterceptor
     */
    public class HandlerExecutionChain {
    
    	private final Object handler;
    
    	private HandlerInterceptor[] interceptors;
    
    	private List<HandlerInterceptor> interceptorList;
    
    

     
    HandlerMappingで現在の要求の処理Handleが見つかった後、HandlerAdapterはこのプロセッサ処理要求を呼び出します.
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    			throws Exception {
    		//      
    		Class<?> clazz = ClassUtils.getUserClass(handler);
    		//             ,
    		Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
    		//        ,         controller    @SessionAttribute  ,         
    		if (annotatedWithSessionAttributes == null) {
    			annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
    			this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
    		}
    		
    		if (annotatedWithSessionAttributes) {
    			
    			checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
    			
    		}
    		else {
    			
    			checkAndPrepare(request, response, true);
    		}
    
    		
    		if (this.synchronizeOnSession) {
    			HttpSession session = request.getSession(false);
    			if (session != null) {
    				Object mutex = WebUtils.getSessionMutex(session);
    				synchronized (mutex) {
    					return invokeHandlerMethod(request, response, handler);
    				}
    			}
    		}
    
    		return invokeHandlerMethod(request, response, handler);
    	}
    
    	//      
    	protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
    			throws Exception {
    
    		ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
    		//      
    		Method handlerMethod = methodResolver.resolveHandlerMethod(request);
    		ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
    		ServletWebRequest webRequest = new ServletWebRequest(request, response);
    		ExtendedModelMap implicitModel = new BindingAwareModelMap();
    		//        
    		Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
    		ModelAndView mav =
    				methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
    		methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
    		return mav;
    	}