SpringMVC工作流程及代码分析

  每谈到SpringMVC的工作流程,首先接触到的就是下面这个图。从这个图可以大致明白SpringMVC是如何工作的。但是我是一个喜欢探究来龙去脉的人,如果不告诉我为什么这么做,单单知道流程就是这样,抱歉,我真的记不住,更不用提里面这么多专业名词了。所以,通过翻阅了源码,大致知道流程是具体怎么实现的,也学到了一些新的设计模式,所以我将阅读源码的所得记录下来,希望本文可以帮助到和我一样喜欢探究来龙去脉的人。

1,SpringMVC是如何被初始化的?

  是通过在web.xml配置文件中配置servlet,servlet的class指向org.springframework.web.servlet.DispatcherServlet,并匹配指定的url(一般情况下使用“/”匹配所有url),指定的请求都会进入DispatcherServlet,由DispatcherServlet进行处理。

<!-- spring mvc核心:分发servlet -->
  <servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- spring mvc的配置文件 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springMVC.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

2,DispatcherServlet的实例化

  为什么要讲DispatcherServlet的初始化,而不是直接说SpringMVC的工作流程,因为在初始化的过程中,建立了HandlerMappings,HandlerAdapters,我会在下面描述这两个如何定义及使用的,有了这些概念,再接触SpringMVC的工作流程,就会非常清晰流畅了。

  Tomcat启动时,就会对DispatcherServlet进行实例化,调用他的init()方法。这里并没有直接在DispatcherServlet中重写init(),而是使用了模板方法模式。抽象类HttpServletBean中定义并实现了init(),FrameworkServlet继承了HttpServletBean,DispatcherServlet继而继承了FrameworkServlet。如果对于模板方法模式不了解,可以查看我的另外一篇文章模板方法模式(Template Method)及应用,也可以自行搜索学习,这里不再赘述。

public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }

        // Set bean properties from init parameters.
        try {
            PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            throw ex;
        }

        // Let subclasses do whatever initialization they like.让子类去实现的方法
        initServletBean();

        if (logger.isDebugEnabled()) {
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }

 2.1 FrameworkServlet的initServeltBean()

@Override
protected final void initServletBean() throws ServletException {
    ...
    try {
        // 初始化 WebApplicationContext (即SpringMVC的IOC容器)
        this.webApplicationContext = initWebApplicationContext();
        initFrameworkServlet();  //这个没有具体实现,如果后续发现会补上
    } catch (ServletException ex) {
    } catch (RuntimeException ex) {
    }
    ...           
}

  2.2 FrameworkServlet的initServeltBean()

protected WebApplicationContext initWebApplicationContext() {
     //获取在web.xml中定义的监听器ContextLoaderListener,初始化并注册在ServeltBean中的根容器,即Spring容器 WebApplicationContext rootContext
= WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it  因为webApplicationContext不为空,说明在构造时已经注入。
wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext);    //将Spring容器设为SpringMVC容器的父容器,这也就是为什么,SpringMVC容器可以调用Spring容器而反过来不可以。 } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { wac = findWebApplicationContext();  //如果为空,则进行查找,能找到就说明上下文已在别处初始化,详见2.3 } if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext);  //如果webApplicationContext仍为空,则以Spring的容器作为父容器建立一个新的。
}
if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. onRefresh(wac);  //模板方法,由DispatcherServlet实现,详见2.4 } if (this.publishContext) { // 发布这个webApplicationContext容器到ServeltContext中。 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }

 2.3 查找webApplicationContext: findWebApplicationContext();

  在servletContext中根据attrName查找

    protected WebApplicationContext findWebApplicationContext() {
        String attrName = getContextAttribute();
        if (attrName == null) {
            return null;
        }
     //从ServletContext中查找已发布的容器 WebApplicationContext wac
= WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName); if (wac == null) { throw new IllegalStateException("No WebApplicationContext found: initializer not registered?"); } return wac; }

 2.4 DispatcherServelt 中的 

  从源码中可以看到,在这个方法中,主要是对各个组件的初始化,包括在整个流程中非常重要的 处理器映射器(HandlerMapping) 和 处理器适配器(HandlerAdapter)

  protected void onRefresh(ApplicationContext context) {
      initStrategies(context); 
  }
  
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }

2.4.1 HanderMapping是什么?

2.4.2 HanderAdapter是什么?

3 DispatcherServelt处理请求

  根据Servlet处理请求的流程,是要调用其doGet(),doPost()方法。DispatcherServelt本质也是个Servlet,所以这两个方法是在其父类FrameworkServelt中实现的。

3.1 FrameworkServelt中的doGet(),doPost()

  protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    @Override
    protected final void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

这些方法都统一调用了processRequest(request, response);

 

3.2  processRequest(request, response)

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;
    // 返回与当前线程相关联的 LocaleContext
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    // 根据请求构建 LocaleContext,公开请求的语言环境为当前语言环境
    LocaleContext localeContext = buildLocaleContext(request);
    
    // 返回当前绑定到线程的 RequestAttributes
    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    // 根据请求构建ServletRequestAttributes
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
    
//WebAsyncManager:用来管理异步请求的处理。什么时候要用到异步处理呢?就是业务逻辑复杂(或者其他原因),为了避免请求线程阻塞,需要委托给另一个线程的时候。
    // 获取当前请求的 WebAsyncManager,如果没有找到则创建
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);    
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

    // 使 LocaleContext 和 requestAttributes 关联
    initContextHolders(request, localeContext, requestAttributes);

    try {
        // 由 DispatcherServlet 实现
        doService(request, response);
    } catch (ServletException ex) {
    } catch (IOException ex) {
    } catch (Throwable ex) {
    } finally {
        // 重置 LocaleContext 和 requestAttributes,解除关联
        resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
            requestAttributes.requestCompleted();
        }// 发布 ServletRequestHandlerEvent 事件
        publishRequestHandledEvent(request, startTime, failureCause);
    }
}

 3.3 DispatcherServetl 中的 doDispatch()

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;    //processedRequest:加工过的请求
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;        //上传相关的?后续补充
    // 获取当前请求的WebAsyncManager,如果没找到则创建并与请求关联
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    try {
        ModelAndView mv = null;
        Exception dispatchException = null;
        try {
            // 检查是否有 Multipart,有则将请求转换为 Multipart 请求
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);
            // 遍历所有的 HandlerMapping 找到与请求对应的 Handler,并将其与一堆拦截器封装到 HandlerExecution 对象中。(如果对handler不理解,不要急,到第二章去看)
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }
            // 遍历所有的 HandlerAdapter,找到可以处理该 Handler 的 HandlerAdapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
            // 处理 last-modified 请求头 
            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;
            }
            try {
                // 执行实际的处理程序
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            } finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
            }
            applyDefaultViewName(request, mv);
            // 遍历拦截器,执行它们的 postHandle() 方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        } catch (Exception ex) {
            dispatchException = ex;
        }
        // 处理执行结果,是一个 ModelAndView 或 Exception,然后进行渲染
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    } catch (Exception ex) {
    } catch (Error err) {
    } finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            // 遍历拦截器,执行它们的 afterCompletion() 方法  
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            return;
        }
        // Clean up any resources used by a multipart request.
        if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
        }
    }
}  

  通过阅读源码,已经可以整理出最开始的流程图和源码之间的对应了,还有一些东西有空会补充上。知其然,更要知其所以然,才是与别人拉开差距的真正意义。

猜你喜欢

转载自www.cnblogs.com/handler4J/p/10055228.html