spring mvc DispatcherServlet详解之二---request通过Controller获取ModelAndView过程

整个spring mvc的架构如下图所示:





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


两个不兼容的类:DispatcherServlet 和Controller 类连接到一起。
  Adapter to use the plain {@link Controller} workflow interface with 
  the generic {@link org.springframework.web.servlet.DispatcherServlet}. 
  Supports handlers that implement the {@link LastModified} interface. 
  <p>This is an SPI class, not used directly by application code.


    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) 
            throws Exception { 
        return ((Controller) handler).handleRequest(request, response); 


 * Base Controller interface, representing a component that receives 
 * {@code HttpServletRequest} and {@code HttpServletResponse} 
 * instances just like a {@code HttpServlet} but is able to 
 * participate in an MVC workflow. Controllers are comparable to the 
 * notion of a Struts {@code Action}. 
 * <p>Any implementation of the Controller interface should be a 
 * <i>reusable, thread-safe</i> class, capable of handling multiple 
 * HTTP requests throughout the lifecycle of an application. To be able to 
 * configure a Controller easily, Controller implementations are encouraged 
 * to be (and usually are) JavaBeans. 
 * </p> 
 * <p><b><a name="workflow">Workflow</a></b></p> 
 * <p> 
 * After a <cde>DispatcherServlet</code> has received a request and has 
 * done its work to resolve locales, themes and suchlike, it then tries 
 * to resolve a Controller, using a 
 * {@link org.springframework.web.servlet.HandlerMapping HandlerMapping}. 
 * When a Controller has been found to handle the request, the 
 * {@link #handleRequest(HttpServletRequest, HttpServletResponse) handleRequest} 
 * method of the located Controller will be invoked; the located Controller 
 * is then responsible for handling the actual request and - if applicable - 
 * returning an appropriate 
 * {@link org.springframework.web.servlet.ModelAndView ModelAndView}. 
 * So actually, this method is the main entrypoint for the 
 * {@link org.springframework.web.servlet.DispatcherServlet DispatcherServlet} 
 * which delegates requests to controllers.</p> 
 * <p>So basically any <i>direct</i> implementation of the Controller interface 
 * just handles HttpServletRequests and should return a ModelAndView, to be further 
 * interpreted by the DispatcherServlet. Any additional functionality such as 
 * optional validation, form handling, etc should be obtained through extending 
 * one of the abstract controller classes mentioned above.</p> 
 * <p><b>Notes on design and testing</b></p> 
 * <p>The Controller interface is explicitly designed to operate on HttpServletRequest 
 * and HttpServletResponse objects, just like an HttpServlet. It does not aim to 
 * decouple itself from the Servlet API, in contrast to, for example, WebWork, JSF or Tapestry. 
 * Instead, the full power of the Servlet API is available, allowing Controllers to be 
 * general-purpose: a Controller is able to not only handle web user interface 
 * requests but also to process remoting protocols or to generate reports on demand.</p> 
 * <p>Controllers can easily be tested by passing in mock objects for the 
 * HttpServletRequest and HttpServletResponse objects as parameters to the 
 * {@link #handleRequest(HttpServletRequest, HttpServletResponse) handleRequest} 
 * method. As a convenience, Spring ships with a set of Servlet API mocks 
 * that are suitable for testing any kind of web components, but are particularly 
 * suitable for testing Spring web controllers. In contrast to a Struts Action, 
 * there is no need to mock the ActionServlet or any other infrastructure; 
 * HttpServletRequest and HttpServletResponse are sufficient.</p> 
 * <p>If Controllers need to be aware of specific environment references, they can 
 * choose to implement specific awareness interfaces, just like any other bean in a 
 * Spring (web) application context can do, for example:</p> 
 * <ul> 
 * <li>{@code org.springframework.context.ApplicationContextAware}</li> 
 * <li>{@code org.springframework.context.ResourceLoaderAware}</li> 
 * <li>{@code org.springframework.web.context.ServletContextAware}</li> 
 * </ul> 
 * <p>Such environment references can easily be passed in testing environments, 
 * through the corresponding setters defined in the respective awareness interfaces. 
 * In general, it is recommended to keep the dependencies as minimal as possible: 
 * for example, if all you need is resource loading, implement ResourceLoaderAware only. 
 * Alternatively, derive from the WebApplicationObjectSupport base class, which gives 
 * you all those references through convenient accessors - but requires an 
 * ApplicationContext reference on initialization. 
 * <p>Controllers can optionally implement the {@link LastModified} interface. 



     * Set if controller execution should be synchronized on the session, 
     * to serialize parallel invocations from the same client. 
     * <p>More specifically, the execution of the {@code handleRequestInternal} 
     * method will get synchronized if this flag is "true". The best available 
     * session mutex will be used for the synchronization; ideally, this will 
     * be a mutex exposed by HttpSessionMutexListener. 
     * <p>The session mutex is guaranteed to be the same object during 
     * the entire lifetime of the session, available under the key defined 
     * by the {@code SESSION_MUTEX_ATTRIBUTE} constant. It serves as a 
     * safe reference to synchronize on for locking on the current session. 
     * <p>In many cases, the HttpSession reference itself is a safe mutex 
     * as well, since it will always be the same object reference for the 
     * same active logical session. However, this is not guaranteed across 
     * different servlet containers; the only 100% safe way is a session mutex. 
     * @see AbstractController#handleRequestInternal 
     * @see org.springframework.web.util.HttpSessionMutexListener 
     * @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession) 


    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) 
            throws Exception { 
        // Delegate to WebContentGenerator for checking and preparing. 
        checkAndPrepare(request, response, this instanceof LastModified); 
        // Execute handleRequestInternal in synchronized block if required. 
        if (this.synchronizeOnSession) { 
            HttpSession session = request.getSession(false); 
            if (session != null) { 
                Object mutex = WebUtils.getSessionMutex(session); 
                synchronized (mutex) { 
                    return handleRequestInternal(request, response); 
        return handleRequestInternal(request, response); 
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) { 
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); 
        String viewName = getViewNameForRequest(request); 
        if (logger.isDebugEnabled()) { 
            logger.debug("Returning view name '" + viewName + "' for lookup path [" + lookupPath + "]"); 
        return new ModelAndView(viewName, RequestContextUtils.getInputFlashMap(request)); 



 * {@link org.springframework.web.servlet.mvc.Controller Controller} 
 * implementation that allows multiple request types to be handled by the same 
 * class. Subclasses of this class can handle several different types of 
 * request with methods of the form 
 * <pre class="code">public (ModelAndView | Map | String | void) actionName(HttpServletRequest request, HttpServletResponse response, [,HttpSession] [,AnyObject]);</pre> 
 * A Map return value indicates a model that is supposed to be passed to a default view 
 * (determined through a {@link org.springframework.web.servlet.RequestToViewNameTranslator}). 
 * A String return value indicates the name of a view to be rendered without a specific model. 
 * <p>May take a third parameter (of type {@link HttpSession}) in which an 
 * existing session will be required, or a third parameter of an arbitrary 
 * class that gets treated as the command (that is, an instance of the class 
 * gets created, and request parameters get bound to it) 
 * <p>These methods can throw any kind of exception, but should only let 
 * propagate those that they consider fatal, or which their class or superclass 
 * is prepared to catch by implementing an exception handler. 
 * <p>When returning just a {@link Map} instance view name translation will be 
 * used to generate the view name. The configured 
 * {@link org.springframework.web.servlet.RequestToViewNameTranslator} will be 
 * used to determine the view name. 
 * <p>When returning {@code void} a return value of {@code null} is 
 * assumed meaning that the handler method is responsible for writing the 
 * response directly to the supplied {@link HttpServletResponse}. 
 * <p>This model allows for rapid coding, but loses the advantage of 
 * compile-time checking. It is similar to a Struts {@code DispatchAction}, 
 * but more sophisticated. Also supports delegation to another object. 
 * <p>An implementation of the {@link MethodNameResolver} interface defined in 
 * this package should return a method name for a given request, based on any 
 * aspect of the request, such as its URL or an "action" parameter. The actual 
 * strategy can be configured via the "methodNameResolver" bean property, for 
 * each {@code MultiActionController}. 
 * <p>The default {@code MethodNameResolver} is 
 * {@link InternalPathMethodNameResolver}; further included strategies are 
 * {@link PropertiesMethodNameResolver} and {@link ParameterMethodNameResolver}. 
 * <p>Subclasses can implement custom exception handler methods with names such 
 * as: 
 * <pre class="code">public ModelAndView anyMeaningfulName(HttpServletRequest request, HttpServletResponse response, ExceptionClass exception);</pre> 
 * The third parameter can be any subclass or {@link Exception} or 
 * {@link RuntimeException}. 
 * <p>There can also be an optional {@code xxxLastModified} method for 
 * handlers, of signature: 
 * <pre class="code">public long anyMeaningfulNameLastModified(HttpServletRequest request)</pre> 
 * If such a method is present, it will be invoked. Default return from 
 * {@code getLastModified} is -1, meaning that the content must always be 
 * regenerated. 
 * <p><b>Note that all handler methods need to be public and that 
 * method overloading is <i>not</i> allowed.</b> 
 * <p>See also the description of the workflow performed by 
 * {@link AbstractController the superclass} (in that section of the class 
 * level Javadoc entitled 'workflow'). 
 * <p><b>Note:</b> For maximum data binding flexibility, consider direct usage of a 
 * {@link ServletRequestDataBinder} in your controller method, instead of relying 
 * on a declared command argument. This allows for full control over the entire 
 * binder setup and usage, including the invocation of {@link Validator Validators} 
 * and the subsequent evaluation of binding/validation errors.*/


    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) 
            throws Exception { 
        try { 
            String methodName = this.methodNameResolver.getHandlerMethodName(request); 
            return invokeNamedMethod(methodName, request, response); 
        catch (NoSuchRequestHandlingMethodException ex) { 
            return handleNoSuchRequestHandlingMethod(ex, request, response); 


protected final ModelAndView invokeNamedMethod( 
            String methodName, HttpServletRequest request, HttpServletResponse response) throws Exception { 
        Method method = this.handlerMethodMap.get(methodName); 
        if (method == null) { 
            throw new NoSuchRequestHandlingMethodException(methodName, getClass()); 
        try { 
            Class<?>[] paramTypes = method.getParameterTypes(); 
            List<Object> params = new ArrayList<Object>(4); 
            if (paramTypes.length >= 3 && paramTypes[2].equals(HttpSession.class)) { 
                HttpSession session = request.getSession(false); 
                if (session == null) { 
                    throw new HttpSessionRequiredException( 
                            "Pre-existing session required for handler method '" + methodName + "'"); 
            // If last parameter isn't of HttpSession type, it's a command. 
            if (paramTypes.length >= 3 && 
                    !paramTypes[paramTypes.length - 1].equals(HttpSession.class)) { 
                Object command = newCommandObject(paramTypes[paramTypes.length - 1]); 
                bind(request, command); 
            Object returnValue = method.invoke(this.delegate, params.toArray(new Object[params.size()])); 
            return massageReturnValueIfNecessary(returnValue); 
        catch (InvocationTargetException ex) { 
            // The handler method threw an exception. 
            return handleException(request, response, ex.getTargetException()); 
        catch (Exception ex) { 
            // The binding process threw an exception. 
            return handleException(request, response, ex); 


     * Processes the return value of a handler method to ensure that it either returns 
     * {@code null} or an instance of {@link ModelAndView}. When returning a {@link Map}, 
     * the {@link Map} instance is wrapped in a new {@link ModelAndView} instance. 
    private ModelAndView massageReturnValueIfNecessary(Object returnValue) { 
        if (returnValue instanceof ModelAndView) { 
            return (ModelAndView) returnValue; 
        else if (returnValue instanceof Map) { 
            return new ModelAndView().addAllObjects((Map<String, ?>) returnValue); 
        else if (returnValue instanceof String) { 
            return new ModelAndView((String) returnValue); 
        else { 
            // Either returned null or was 'void' return. 
            // We'll assume that the handle method already wrote the response. 
            return null; 


   DispatcherServlet接受一个请求,然后解析完locales, themes等后,通过HadlerMapping解析控制器Controller去处理请求。


  DispatcherServlet 代理此Controller,接收返回结果,然后进行渲染。




微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!