zoukankan      html  css  js  c++  java
  • spring mvc DispatcherServlet详解之二---request通过Controller获取ModelAndView过程

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

    上篇文件讲解了DispatcherServlet通过request获取控制器Controller的过程,现在来讲解DispatcherServletDispatcherServlet的第二步:通过request从Controller获取ModelAndView。

    DispatcherServlet调用Controller的过程:

    DispatcherServlet.java

    doService()--->doDispatch()--->handlerAdapter的handle()方法

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

    最常用的实现了HandlerAdapter接口是SimpleControllerHandlerAdapter类,该类将

    两个不兼容的类: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.

    类之间的转换代码如下所示,调用了Controller类的handleRequest()方法来处理请求:

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

    重量级人物控制器Controller开始闪亮登场,Controller是一个基本的接口,它接受request和response,从这点上来说,它有点像servlet,但不同之处在于它在mvc模式流程中起作用,它和struts中的Action作用类似。继承该接口的控制器或者类应该保证是线程安全的,可复用的,能够在一个应用生命周期中处理大量的request。为了使Controller的配置更便捷,通常使用javaBeans来继承Controller。

    /**
     * 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.
    */

    Controller的handleRequest()方法处理请求,并返回ModelAndView给DispatcherServlet去渲染render。

    Controller接口的抽象实现类为:AbstractController,它通过互斥锁(mutex)来保证线程安全。

        /**
         * 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);
        }
    handleRequestInternal()为抽象方法,留待具体实现类来实现。它的直接子类有:

    简单Controller实现
    在web.xml中有时候定义节点<welcome-list>index.html</welcome-list>等,这种简单的请,Controller是如何实现的呢?我们来看看UrlFilenameViewController,它是Controller的一个间接实现,实现了AbstractUrlViewController。它把url的虚拟路径转换成一个view的名字,然后返回这个view。
        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));
        }

    复杂Controller实现

    一个可以处理多种请求类型的Controller实现:MultiActionController。它类似于struts中的DispatcherAction,但更灵活,而且支持代理。

    /**
     * {@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.*/

     根据方法名决定处理的handler

        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);
                params.add(request);
                params.add(response);
    
                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 + "'");
                    }
                    params.add(session);
                }
    
                // 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]);
                    params.add(command);
                    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);
            }

    处理返回结果,要么返回null要么返回ModelAndView实例。当返回一个Map类型时,ModelAndView实例包装的Map类型。

        /**
         * 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.
         */
        @SuppressWarnings("unchecked")
        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去处理请求。

       找到Controller后,出发当前controller的handleRequest()方法,此controller负责真正处理请求,然后一个ModelAndView实例。

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




  • 相关阅读:
    Chandy-Lamport_algorithm
    3 differences between Savepoints and Checkpoints in Apache Flink
    列数 行数 表数 限制
    数据收集、传输、元数据管理、作业流调度、海量数据查询引擎、数据可视化
    分析云负载均衡产品
    端口被占用通过域名的处理 把www.domain.com均衡到本机不同的端口 反向代理 隐藏端口 Nginx做非80端口转发 搭建nginx反向代理用做内网域名转发 location 规则
    JSON Web Token
    查看开启端口的应用
    If the parts of an organization (e.g., teams, departments, or subdivisions) do not closely reflect the essential parts of the product, or if the relationship between organizations do not reflect the r
    微服务架构的理论基础
  • 原文地址:https://www.cnblogs.com/davidwang456/p/4119314.html
Copyright © 2011-2022 走看看