zoukankan      html  css  js  c++  java
  • SpringMVC解析4-DispatcherServlet逻辑脉络

    HttpServlet提供了不同的服务方法,它们是doDelete(),doGet(),doOptions(),doPost(),doPut(),和doTrace(),它会根据不同的请求形式将程序引导至对应的函数进行处理。这几个函数中最常用的函数无非就是doGet()和doPost(),我们看看DispatcherServlet对着两个函数的逻辑实现。

    @Override  
    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);  
    }  
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)  
            throws ServletException, IOException {  
        //记录当前时间,用于计算web请求的处理时间  
        long startTime = System.currentTimeMillis();  
        Throwable failureCause = null;  
        //根据当前request创建对应的LocaleContext和RequestAttributes,并绑定到当前线程  
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();  
        LocaleContext localeContext = buildLocaleContext(request);  
      
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();  
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);  
      
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);  
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());  
      
        initContextHolders(request, localeContext, requestAttributes);  
      
        try {  
            //委托给doService方法进一步处理  
            doService(request, response);  
        }  
        catch (ServletException ex) {  
            failureCause = ex;  
            throw ex;  
        }  
        catch (IOException ex) {  
            failureCause = ex;  
            throw ex;  
        }  
        catch (Throwable ex) {  
            failureCause = ex;  
            throw new NestedServletException("Request processing failed", ex);  
        }  
      
        finally {  
            //请求处理结束后回复线程到原始状态  
            resetContextHolders(request, previousLocaleContext, previousAttributes);  
            if (requestAttributes != null) {  
                requestAttributes.requestCompleted();  
            }  
      
            if (logger.isDebugEnabled()) {  
                if (failureCause != null) {  
                    this.logger.debug("Could not complete request", failureCause);  
                }  
                else {  
                    if (asyncManager.isConcurrentHandlingStarted()) {  
                        logger.debug("Leaving response open for concurrent processing");  
                    }  
                    else {  
                        this.logger.debug("Successfully completed request");  
                    }  
                }  
            }  
            //请求处理结束后无论成功与否发布事件通知  
            publishRequestHandledEvent(request, startTime, failureCause);  
        }  
    }  

    函数中已经开始了对请求的处理,虽然把细节转移到了doService函数中实现,但也看得出处理请求前后所做的准备工作。

    (1)为了保证当前线程的LocaleContext以及RequestAttributes可以在当前请求后还能恢复,提取当前线程的两个属性。

    (2)根据当前request创建对应的LocaleContext和RequestAttributes,并绑定到当前线程。

    (3)委托给doService方法进一步处理。

    (4)请求处理结束后恢复线程到原始状态。

    (5)请求处理结束后无论成功与否发布事件通知

    继续查看doService方法。

    @Override  
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {  
        if (logger.isDebugEnabled()) {  
            String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";  
            logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +  
                    " processing " + request.getMethod() + " request for [" + getRequestUri(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<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 {  
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {  
                // Restore the original attribute snapshot, in case of an include.  
                if (attributesSnapshot != null) {  
                    restoreAttributesAfterInclude(request, attributesSnapshot);  
                }  
            }  
        }  
    }  

    我们猜想对请求处理至少应该包括一些诸如寻找handler并页面跳转之类的逻辑处理,但是,在doService中我们并没有看到想看到的逻辑,相反却同样是一些准备工作,但是这些准备工作是必不可少的。Spring将已经初始化的功能辅助工具变量,比如localeResolver,themeResolver等设置在request属性中,而这些属性会在接下来派上用场。doDispatch函数中展示了Spring请求处理所涉及的主要逻辑,而我们之前设置在request中的各种辅助属性也都有被派上了用场。

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  
        HttpServletRequest processedRequest = request;  
        HandlerExecutionChain mappedHandler = null;  
        boolean multipartRequestParsed = false;  
      
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);  
      
        try {  
            ModelAndView mv = null;  
            Exception dispatchException = null;  
      
            try {  
                //如果是MultipartContent类型的request则转换request为MultipartHttpServletRequest类型的request  
                processedRequest = checkMultipart(request);  
                multipartRequestParsed = (processedRequest != request);  
      
                // Determine handler for the current request.  
                //根据request信息寻找对应的handler  
                mappedHandler = getHandler(processedRequest, false);  
                if (mappedHandler == null || mappedHandler.getHandler() == null) {  
                    //如果没有找到对应的handler则通过response反馈错误信息  
                    noHandlerFound(processedRequest, response);  
                    return;  
                }  
      
                // Determine handler adapter for the current request.  
                //根据当前的handler寻找对应的handlerAdapter  
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  
      
                // Process last-modified header, if supported by the handler.  
                String method = request.getMethod();  
                //如果handler支持last-modified头处理  
                boolean isGet = "GET".equals(method);  
                if (isGet || "HEAD".equals(method)) {  
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());  
                    if (logger.isDebugEnabled()) {  
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);  
                    }  
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {  
                        return;  
                    }  
                }  
                //拦截器的preHandler方法的调用  
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {  
                    return;  
                }  
                // Actually invoke the handler.  
                //真正的激活handler并返回视图  
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  
      
                if (asyncManager.isConcurrentHandlingStarted()) {  
                    return;  
                }  
                //视图名称转换应用于需要添加前缀后缀的情况  
                applyDefaultViewName(request, mv);  
                //调用所有拦截器的postHandle方法  
                mappedHandler.applyPostHandle(processedRequest, response, mv);  
            }  
            catch (Exception ex) {  
                dispatchException = ex;  
            }  
            //页面处理  
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  
        }  
        catch (Exception ex) {  
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);  
        }  
        catch (Error err) {  
            triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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);  
                }  
            }  
        }  
    }  
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,  
            HandlerExecutionChain mappedHandler, ModelAndView mv, 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.isDebugEnabled()) {  
                logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +  
                        "': assuming HandlerAdapter completed request handling");  
            }  
        }  
        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {  
            // Concurrent handling started during a forward  
            return;  
        }  
        if (mappedHandler != null) {  
            //完成激活处理触发器  
            mappedHandler.triggerAfterCompletion(request, response, null);  
        }  
    }  

    Spring每一步是怎么处理dispatcher中的方法的,在下面一节分析。

  • 相关阅读:
    关于线程池的线程复用
    Java线程锁之意难平的读写锁
    Java8之StringJoiner
    springboot整合thymeleaf
    一维数组转二叉树、注解回滚、eclipse配置代码自动补全
    Java之线程锁
    关于工作中的一些总结
    关于shiro的猜测
    Java之扫描不到mapper
    网页中引用css样式
  • 原文地址:https://www.cnblogs.com/wade-luffy/p/6085381.html
Copyright © 2011-2022 走看看