zoukankan      html  css  js  c++  java
  • SpringMVC探秘-请求之路

    SpringMVC探秘-请求之路

    开始

    今天来分析一下SpringMVC的原理,探究SpringMVC如何把请求传递到每个Controller的方法上,从Servlet到Controller,一个请求走了哪些艰难曲折的路。

    基本核心组件

    在开始分析之前,先了解SpringMVC中的几个概念,后面提到的时候不至于不知道这个是干什么用的,不过不用彻底知道这个组件是如何实现的,现在只需要知道有这个东西就行了, 先混个脸熟

    组件 说明
    DispatcherServlet SpringMVC中的中心,处理请求的分发,视图的解析等,本质上是一个Servlet
    HandlerMapping 一个映射,<URL,Handler>,可以根据URL找到匹配的Handler,这儿的Handler是HandlerExecutionChain
    HandlerAdapter Hander的适配器,委托这个适配器去调用具体的某个Hander
    ViewResolver 视图解析器,用于将视图名称解析为视图对象 View。

    这些组件都是经过DispatcherServlet的协调,把一个请求分发到具体的一个Hander,然后根据Hander的返回值,来做不同的处理,不同的组件处理不同的逻辑。

    追根溯源

    请求的入口-Servlet

    我们都知道Servlet是一个标准,我们的处理逻辑代码只需要写到Servlet中,而对于Http请求响应的解析处理都不需要我们关心,已经有很多服务器实现了这部分,比如熟悉的小猫猫Tomcat,它就是按照Servlet的标准来实现的。

    ServletConfig

    /**
     * A servlet configuration object used by a servlet container to pass
     * information to a servlet during initialization.
     * 当servlet实例化的时候,servlet容器传递给servlet的配置
     */
    public interface ServletConfig {
    
        /**
         * Returns the name of this servlet instance. The name may be provided via
         * server administration, assigned in the web application deployment
         * descriptor, or for an unregistered (and thus unnamed) servlet instance it
         * will be the servlet's class name.
         * 返回servlet的名字,如果没有指定,则返回servlet的类名
         * @return the name of the servlet instance
         */
        public String getServletName();
    
        /**
         * Returns a reference to the {@link ServletContext} in which the caller is
         * executing.
         * 返回调用者当前执行环境的ServletContext引用
         * @return a {@link ServletContext} object, used by the caller to interact
         *         with its servlet container
         * @see ServletContext
         */
        public ServletContext getServletContext();
    
        /**
         * Returns a <code>String</code> containing the value of the named
         * initialization parameter, or <code>null</code> if the parameter does not
         * exist.
         * 返回一个字符串类型的值,这个值是初始化的参数
         * @param name
         *            a <code>String</code> specifying the name of the
         *            initialization parameter
         * @return a <code>String</code> containing the value of the initialization
         *         parameter
         */
        public String getInitParameter(String name);
    
        /**
         * Returns the names of the servlet's initialization parameters as an
         * <code>Enumeration</code> of <code>String</code> objects, or an empty
         * <code>Enumeration</code> if the servlet has no initialization parameters.
         * 返回所有的初始化参数
         * @return an <code>Enumeration</code> of <code>String</code> objects
         *         containing the names of the servlet's initialization parameters
         */
        public Enumeration<String> getInitParameterNames();
    }
    

    Servlet

    package javax.servlet;
    
    import java.io.IOException;
    
    /**
     * 所有的servlet都应该实现这里的方法
     *
     * servlet是一个运行在Web Server的小Java程序,它们接受对来自web客户端的请求并做出响应,这些请求通常通过HTTP传达
     *
     * 为了实现这个接口,你可以写一个基于javax.servlet.GenericServlet,或者HTTP     Servletjavax.servlet.http.HttpServlet派生的servlet类
     *
     * 这个接口定义了实例化servlet,接受请求,从server移除的方法,这些方法被称为生命周期方法:并且按照一些的顺序调用:
     * 1. servlet被实例化,调用init()方法初始化
     * 2. 当有来自客户端的调用,serviceI()方法被调用
     * 3. servlet退出服务,destory()方法被调用,然后执行GC
     *
     * 除了生命周期方法外,这个接口还提供了getServletConfig方法来获取启动信息,getServletInfo来获取
     * servlet自身的基本信息,比如作者,版本,版权等
     *
     * @see GenericServlet
     * @see javax.servlet.http.HttpServlet
     */
    public interface Servlet {
    
        /**
         * 被servlet容器调用,表示这个servlet将要开始服务
    	 *
         * servlet实例化之后马上调用这个方法,在开始处理请求前这个方法必须调用成功
         * 
         * 如果这个方法抛出异常或者在指定的时间段没有返回,那么这个serlet也不能处理请求
         * @exception ServletException
         *                if an exception has occurred that interferes with the
         *                servlet's normal operation
         *
         * @see UnavailableException
         * @see #getServletConfig
         */
        public void init(ServletConfig config) throws ServletException;
    
        /**
       	 * 获取ServletConfig,存储了启动参数
         * @return the <code>ServletConfig</code> object that initializes this
         *         servlet
         *
         * @see #init
         */
        public ServletConfig getServletConfig();
    
        /**
         * 被Servlet容器调用,用来对一个请求做出响应
      	 *
         * 只有当servlet中的init()方法调用成功后这个方法才会调用
         *
         * 应该为抛出异常或者错误的servlet返回的response设置响应码
         * 
         * 这个方法可能被并发调用,所以开发者对于共享的资源要同步操作,共享的资源比如,文件,网络连接,
         * 还有servlet的类变量和实例变量
         * @param req
         *            the <code>ServletRequest</code> object that contains the
         *            client's request
         *
         * @param res
         *            the <code>ServletResponse</code> object that contains the
         *            servlet's response
         *
         * @exception ServletException
         *                if an exception occurs that interferes with the servlet's
         *                normal operation
         *
         * @exception IOException
         *                if an input or output exception occurs
         */
        public void service(ServletRequest req, ServletResponse res)
                throws ServletException, IOException;
    
        /**
    	 * 获取Servlet基本信息
         *
         * @return a <code>String</code> containing servlet information
         */
        public String getServletInfo();
    
        /**
         * 这个方法被调用,表明这个servlet已经不再处理请求,这个方法在多线程环境也只调用一次,调用后,
         * service方法将不会再调用
         *
         * 给一个servlet释放资源的机会,比如文件,还有需要持久化的资源需和内存中的状态同步
         */
        public void destroy();
    }
    

    GenericServlet

    GenericServlet 实现了 Servlet 和 ServletConfig 两个接口,为这两个接口中的部分方法提供了简单的实现。比如该类实现了 Servlet 接口中的 void init(ServletConfig) 方法,并在方法体内调用了内部提供了一个无参的 init 方法,子类可覆盖该无参 init 方法。除此之外,GenericServlet 还实现了 ServletConfig 接口中的 getInitParameter 方法,用户可直接调用该方法获取到配置信息。而不用先获取 ServletConfig,然后再调用 ServletConfig 的 getInitParameter 方法获取

    GenericServlet 是一个协议无关的 servlet,是一个比较原始的实现,通常我们不会直接继承该类。一般情况下,我们都是继承 GenericServlet 的子类 HttpServlet,该类是一个和 HTTP 协议相关的 Servlet。那下面我们来看一下这个类

    HttpServlet

    HttpServlet,从名字上就可看出,这个类是和 HTTP 协议相关。该类的关注点在于怎么处理 HTTP 请求,比如其定义了 doGet 方法处理 GET 类型的请求,定义了 doPost 方法处理 POST 类型的请求等。我们若需要基于 Servlet 写 Web 应用,应继承该类,并覆盖指定的方法。doGet 和 doPost 等方法并不是处理的入口方法,所以这些方法需要由其他方法调用才行。其他方法是哪个方法呢?对,在Servlet接口定义处理请求的方法service中。当然在HttpServlet中还有一些处理缓存的逻辑

    请求分发中央-DispatcherServlt

    在认识它之前,介绍一下它在家族中的地位

    对于Servlet相关的类和接口上面已经介绍过了,这里多了一些以Aware结尾的接口,是Spring IOC中的接口,

    HttpServletBean

    重写了GenericServlet中的init()方法,设置servlet配置的初始化参数

    FrameworkServlet

    FrameworkServlet 是 Spring Web 框架中的一个基础类,该类会在初始化时创建一个容器。同时该类覆写了 doGet、doPost 等方法,并将所有类型的请求委托给 doService 方法去处理。doService 是一个抽象方法,需要子类实现。

    servlet被实例化后,会调用GenericServlet的init(ServletConfig config)方法,然后会无参init()方法,HttpServletBean重写了init()方法,这个方法会调用交给子类去实现的initServletBean()方法,现在就到了FrameworkServlet中的initServletBean(),该方法会调用initWebApplicationContext(),这里创建多个容器我,他们之间的关系后面我们再说

    DispatcherServlet

    DispatcherServlet就是核心的一个组件,来负责协调各个组件,同事初始化这些组件如,HanderMapping,HanderAdapter等。

    中央处理器DispatcherServlet

    我们继续从上面的FrameworkServlet调用的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 {
    		logRequest(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<>();
    			Enumeration<?> attrNames = request.getAttributeNames();
    			while (attrNames.hasMoreElements()) {
    				String attrName = (String) attrNames.nextElement();
    				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
    					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());
    
    		if (this.flashMapManager != null) {
    			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()) {
    				// Restore the original attribute snapshot, in case of an include.
    				if (attributesSnapshot != null) {
    					restoreAttributesAfterInclude(request, attributesSnapshot);
    				}
    			}
    		}
    	}
    

    doService中就是给request设置一些参数,然后调用doDispatch()方法。doDispatch()才是真正处理请求的方法

    	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		HttpServletRequest processedRequest = request;
            // 这个对象包含HnadlerInterceptor集合和Handler对象
    		HandlerExecutionChain mappedHandler = null;
    		boolean multipartRequestParsed = false;
    
    		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
    		try {
    			ModelAndView mv = null;
    			Exception dispatchException = null;
    
    			try {
                    // 检查是否是Multipart request,如果是并且有MultipartResolver,则返回MultipartResolver处理后的request
    				processedRequest = checkMultipart(request);
                    // 是否被MultipartResolver处理
    				multipartRequestParsed = (processedRequest != request);
    
    				// Determine handler for the current request.
                    // 从HanderMapping获取HandlerExecutionChain
    				mappedHandler = getHandler(processedRequest);
    				if (mappedHandler == null) {
    					noHandlerFound(processedRequest, response);
    					return;
    				}
    
    				// Determine handler adapter for the current request.
                    // 获取可执行处理器逻辑的适配器 HandlerAdapter
    				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    				// Process last-modified header, if supported by the handler.
                    // HTTP缓存相关
    				String method = request.getMethod();
    				boolean isGet = "GET".equals(method);
    				if (isGet || "HEAD".equals(method)) {
    					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
    					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    						return;
    					}
    				}
    				
                    // 执行拦截器 preHandle 方法
    				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    					return;
    				}
    
    				// 调用处理器逻辑,对应步骤
    				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    				if (asyncManager.isConcurrentHandlingStarted()) {
    					return;
    				}
    
                    // 如果 controller 未返回 view 名称,这里生成默认的 view 名称
    				applyDefaultViewName(processedRequest, mv);
                    // 执行拦截器 postHandle 方法
    				mappedHandler.applyPostHandle(processedRequest, response, mv);
    			}
    			catch (Exception ex) {
    				dispatchException = ex;
    			}
    			catch (Throwable err) {
    				// As of 4.3, we're processing Errors thrown from handler methods as well,
    				// making them available for @ExceptionHandler methods and other scenarios.
    				dispatchException = new NestedServletException("Handler dispatch failed", err);
    			}
               // 解析并渲染视图
    			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    		}
    		catch (Exception ex) {
    			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    		}
    		catch (Throwable err) {
    			triggerAfterCompletion(processedRequest, response, mappedHandler,
    					new NestedServletException("Handler processing failed", err));
    		}
    		finally {
    			if (asyncManager.isConcurrentHandlingStarted()) {
    				// Instead of postHandle and afterCompletion
    				if (mappedHandler != null) {
    					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
    				}
    			}
    			else {
    				// Clean up any resources used by a multipart request.
    				if (multipartRequestParsed) {
    					cleanupMultipart(processedRequest);
    				}
    			}
    		}
    	}
    

    getHander(HttpServletRequest)

    	/**
    	 * 遍历所有的HanderMapping,获取一个HanderExecutionChian
    	 * Return the HandlerExecutionChain for this request.
    	 * <p>Tries all handler mappings in order.
    	 * @param request current HTTP request
    	 * @return the HandlerExecutionChain, or {@code null} if no handler could be found
    	 */
    	@Nullable
    	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    		if (this.handlerMappings != null) {
    			for (HandlerMapping mapping : this.handlerMappings) {
    				HandlerExecutionChain handler = mapping.getHandler(request);
    				if (handler != null) {
    					return handler;
    				}
    			}
    		}
    		return null;
    	}
    

    getHandlerAdapter

    /**
     * Return the HandlerAdapter for this handler object.
     * @param handler the handler object to find an adapter for
     * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
     */
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
       if (this.handlerAdapters != null) {
          for (HandlerAdapter adapter : this.handlerAdapters) {
             if (adapter.supports(handler)) {
                return adapter;
             }
          }
       }
       throw new ServletException("No adapter for handler [" + handler +
             "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }
    

    processDispatchResult

    /**
     * Handle the result of handler selection and handler invocation, which is
     * either a ModelAndView or an Exception to be resolved to a ModelAndView.
     */
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
          @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
          @Nullable Exception exception) throws Exception {
    
       boolean errorView = false;
    
       if (exception != null) {
          if (exception instanceof ModelAndViewDefiningException) {
             logger.debug("ModelAndViewDefiningException encountered", exception);
             mv = ((ModelAndViewDefiningException) exception).getModelAndView();
          }
          else {
             Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
             mv = processHandlerException(request, response, handler, exception);
             errorView = (mv != null);
          }
       }
    
       // Did the handler return a view to render?
       if (mv != null && !mv.wasCleared()) {
          render(mv, request, response);
          if (errorView) {
             WebUtils.clearErrorRequestAttributes(request);
          }
       }
       else {
          if (logger.isTraceEnabled()) {
             logger.trace("No view rendering, null ModelAndView returned.");
          }
       }
    
       if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
          // Concurrent handling started during a forward
          return;
       }
    
       if (mappedHandler != null) {
          mappedHandler.triggerAfterCompletion(request, response, null);
       }
    }
    

    后记

    整个请求的流程就走完了,但是对于HandlerMapping和HandlerAdapter,如何渲染视图这块没有详细说,因为现在我也还不太懂,等后面会再写文章来说明。

    再DispatcherServlet初始化后,会创建容器和创建组件,这些在本文中也没有说明,篇幅限制就另外写一篇来做解释了。

  • 相关阅读:
    JS基础语法
    JS的初步了解
    CSS初步学习
    HTML标签
    初步了解HTML
    LEGB规则
    Python面试题练习
    闭包
    Caché,Cache数据库连接并查询数据
    Caché,Cache数据库下载
  • 原文地址:https://www.cnblogs.com/watertreestar/p/12386655.html
Copyright © 2011-2022 走看看