美文网首页
Java面试:SpringMVC工作流程

Java面试:SpringMVC工作流程

作者: 程序员驴子酱 | 来源:发表于2021-12-14 21:06 被阅读0次

    1.SpringMVC简介

    SpringMVC是一种基于Spring实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,使用了MVC架构模式的思想,将web层进行职责解耦,并管理应用所需对象的生命周期,为简化日常开发,提供了很大便利。
    
    SpringMVC提供的核心组件包括请求分发器(DispatchServlet)、请求处理映射器(HandlerMapping)、请求处理适配器(Handler Adapter)、视图解析器(View Resolver) 、动作处理器(Controller)和模型视图对象(ModelAndView),通过以上各大组件的协同工作来完成客户端的请求响应工作,使得MVC架构的设计理念得以实现。
    

    2.SpringMVC工作流程

    我们先来看一下SpringMVC的流程图
    
    222.png

    (1)客户端向web服务器(如tomcat)发送一个http请求,web服务器对http请求进行解析,解析后的url地址如果匹配到DispatchServlet的映射路径(通过web.xml中的servlet-mapping配置),web容器就会将请求交给DispatchServlet处理

    (2)DispatcherServlet接收到这个请求后,再对URL进行解析,得到请求资源标识符(URI)。然后调用相应方法得到的HandlerMapping对象,再根据URI,调用这个对象的相应方法获得Handler对象以及它对应的拦截器。(在这里只是获得了Handler对象,并不会操作它,在SpringMVC中,是通过HandlerAdapter对Handler进行调用、控制的)

    (3)DispatcherServlet根据得到的Handler对象,选择一个合适的HandlerAdapter,创建其实例对象,执行拦截器中的preHandler()方法。

    (4)在拦截器方法中,提取请求中的数据模型,填充Handler入参,所以所有准备工作都已做好,开始执行Handler(我们写的controller代码并不是能被直接执行,需要有刚才那些操作,才能转变为Handler被执行)。

    (5)Handler执行完毕后返回一个ModelAndView对象给DispatcherServlet。

    (6)这个ModleAndView只是一个逻辑视图,并不是真正的视图,DispatcherServlet通过ViewResolver视图解析器将逻辑视图转化为真正的视图(通俗理解为将视图名称补全,如加上路径前缀,加上.jsp后缀,能指向实际的视图)。

    (7)DispatcherServlet通过Model将ModelAndView中得到的处数据解析后用于渲染视图。将得到的最终视图通过http响应返回客户端。

    3.SpringMVC组件解析

    3.1 HandlerMapping

     SpringMVC体系结构中有着举足轻重的地位,充当着url和Controller之间映射关系配置的角色。主要有三部分组成:HandlerMapping映射注册、根据url获取对应的处理器、拦截器注册。
    
    444.gif

    在Spring MVC中,关于HandlerMapping的使用,主要包括两个部分:注册和查找。在HandlerMapping的实现中,持有一个handlerMap这样一个HashMap<String, Object>,其中key是http请求的path信息,value可以是一个字符串,或者是一个处理请求的HandlerExecutionChain,如果是String类型,则会将其视为Spring的bean名称。在HandlerMapping对象的创建中,IoC容器执行了一个容器回调方法setApplicationContext,在这个方法中调用initApplicationContext方法进行初始化,各个子类可以根据需求的不同覆写这个方法。关于handlerMap信息的注册就是在initApplicationContext方法中被执行的。

    3.2 Handleradapter

     该组件在SpringMVC请求执行过程中包括两个部分,HandlerAdapter的注册和HandlerAdapter的执行。
    

    (1)HandlerAdapter的注册:

    DispatcherServlte会根据配置文件信息注册HandlerAdapter,如果在配置文件中没有配置,那么DispatcherServlte会获取HandlerAdapter的默认配置,如果是读取默认配置的话,DispatcherServlte会读取DispatcherServlte.properties文件,该文件中配置了三种HandlerAdapter:HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter。DispatcherServlte会将这三个HandlerAdapter对象存储到它的handlerAdapters这个集合属性中,这样就完成了HandlerAdapter的注册。
    

    (2)HandlerAdapter的执行:

    DispatcherServlte会根据handlerMapping传过来的controller与已经注册好了的HandlerAdapter一一匹配,看哪一种HandlerAdapter是支持该controller类型的,如果找到了其中一种HandlerAdapter是支持传过来的controller类型,那么该HandlerAdapter会调用自己的handle方法,handle方法运用java的反射机制执行controller的具体方法来获得ModelAndView,例如SimpleControllerHandlerAdapter是支持实现了controller接口的控制器,如果自己写的控制器实现了controller接口,那么SimpleControllerHandlerAdapter就会去执行自己写控制器中的具体方法来完成请求。
    

    4.SpringMVC源码简单剖析

    根据上边的图我们可以看到前端控制器在springMVC的系统中拥有了非常大的作用,那么我们就以此来入手。


    333.png
    • 首先在它的关系图中我们可以看出,DispatcherServlet类继承FrameworkServlet类,而FrameworkServlet继承了HttpServletBean类,再往上就是HttpServlet类与Servlet类了。
    • 那么我们就可以得出DispatchServlet是一个servlet类,既然是一个Servlet那么久有一个核心方法:Service()方法,我们在DispatchServlet中找,发现没有,那我们继续去它的父类中找,于是发现了如下方法:
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if (HttpMethod.PATCH != httpMethod && httpMethod != null) {
            super.service(request, response);
        } else {
            this.processRequest(request, response);
        }
    }
    
    • 它首先判断是不是PATCH请求,如果是的话执行父类的service()方法,如果不是执行 processRequest(),那我们继续追踪:
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            long startTime = System.currentTimeMillis();
            Throwable failureCause = null;
            LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
            LocaleContext localeContext = this.buildLocaleContext(request);
            RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));
            this.initContextHolders(request, localeContext, requestAttributes);
            try {
                this.doService(request, response);
            } catch (IOException | ServletException var16) {
                failureCause = var16;
                throw var16;
            } catch (Throwable var17) {
                failureCause = var17;
                throw new NestedServletException("Request processing failed", var17);
            } finally {
                this.resetContextHolders(request, previousLocaleContext, previousAttributes);
                if (requestAttributes != null) {
                    requestAttributes.requestCompleted();
                }
    
                if (this.logger.isDebugEnabled()) {
                    if (failureCause != null) {
                        this.logger.debug("Could not complete request", (Throwable)failureCause);
                    } else if (asyncManager.isConcurrentHandlingStarted()) {
                        this.logger.debug("Leaving response open for concurrent processing");
                    } else {
                        this.logger.debug("Successfully completed request");
                    }
                }
    
                this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
            }
        }
    
    • 这里边的代码主要是执行一些控制器等等,可以不用了解太多,我们主要是了解其执行流程。在其中我们可以看到 this.doService(request, response);方法,来执行一些事务,那么我们继续追踪:
    protected abstract void doService(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
    
    
    • 我们发现它是调用了子类:DispatcherServlet类中的方法,继续追踪:
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (this.logger.isDebugEnabled()) {
            String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
            this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
        }
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap();
            Enumeration attrNames = request.getAttributeNames();
    
            label112:
            while(true) {
                String attrName;
                do {
                    if (!attrNames.hasMoreElements()) {
                        break label112;
                    }
                    attrName = (String)attrNames.nextElement();
                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
    
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.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 {
            this.doDispatch(request, response);
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }
    
        }
    }
    
    
    • 我们看到它首先设置了一些request属性,然后执行了一个this.doDispatch(request, response)方法,至此我们算是找到了它的核心执行代码:
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;
                try {
                    //这个方法下面有单独说明
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                     //这个方法下面有单独说明
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
                    //这个方法下面有单独说明
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if (this.logger.isDebugEnabled()) {
                            this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                        }
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
                     //这个方法下面有单独说明
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
                     //这个方法下面有单独说明
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
                    this.applyDefaultViewName(processedRequest, mv);
                    //这个方法下面有单独说明
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }
                //这个方法下面有单独说明
                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }
    
        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }
    
        }
    }
    
    • 这段代码可以分成四个部分:

    ①查找Handler对应的HandlerMapping部分:

    try {
        processedRequest = this.checkMultipart(request);
        multipartRequestParsed = processedRequest != request;
        mappedHandler = this.getHandler(processedRequest);
        if (mappedHandler == null) {
            this.noHandlerFound(processedRequest, response);
            return;
        }
    

    它首先判断request请求是不是二进制请求,然后执行this.getHandler(processedRequest);方法。那么我们来了解一下这个方法:

    ②this.getHandler(processedRequest)部分

    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            Iterator var2 = this.handlerMappings.iterator();
            while(var2.hasNext()) {
                HandlerMapping hm = (HandlerMapping)var2.next();
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");
                }
                HandlerExecutionChain handler = hm.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }
    
    

    首先:我们发现它获取了一个HandlerMapping的迭代器,然后查找每一个HandlerMapping。那么它为什么要先去查找HandlerMapping呢?那是因为我们在配置Controller的时候有两种方式:xml和注解的方式,所以它先去找到HandlerMapping,然后根据HandlerMapping去找Handler,就是hm.getHandler(request);这个方法来查找Handler。

    ③查找HandlerAdapter部分:

    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
    String method = request.getMethod();
    boolean isGet = "GET".equals(method);
    if (isGet || "HEAD".equals(method)) {
        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
        }
        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
            return;
        }
    }
    

    ④在拿到HandlerMapping之后,开始查找HandlerAdapter,就是ha.getLastModified(request, mappedHandler.getHandler());来查找。

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
            if (this.handlerAdapters != null) {
                Iterator var2 = this.handlerAdapters.iterator();
                while(var2.hasNext()) {
                    HandlerAdapter ha = (HandlerAdapter)var2.next();
                    if (this.logger.isTraceEnabled()) {
                        this.logger.trace("Testing handler adapter [" + ha + "]");
                    }
                    if (ha.supports(handler)) {
                        return ha;
                    }
                }
            }
            throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
        }
    
    

    它的查找方式与之前查找HandlerMapping类似。

    • 拦截器执行部分:
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        return;
    }
    
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    this.triggerAfterCompletion(request, response, (Exception)null);
                    return false;
                }
            }
        }
        return true;
    }
    
    

    以上一小段代码就是获取拦截器然后执行。

    • 再接下来就是最重要的Hanler执行部分,它返回的对象是mv,就是ModelAndView对象
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    if (asyncManager.isConcurrentHandlingStarted()) {
        return;
    }
    this.applyDefaultViewName(processedRequest, mv);
    mappedHandler.applyPostHandle(processedRequest, response, mv);
    } catch (Exception var20) {
        dispatchException = var20;
    } catch (Throwable var21) {
        dispatchException = new NestedServletException("Handler dispatch failed", var21);
    }
    //这个方法下面有说明
    this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
    

    它首先在 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 执行了handler并返回了一个视图;然后通过 this.applyDefaultViewName(processedRequest, mv); 在我们没有设置视图名称的时候返回默认视图名;然后通过 this.applyDefaultViewName(processedRequest, mv); 获取到项目的拦截器,然后判断拦截器是否存在,存在则对该请求进行测试(postHandler),如果有一个拦截器不成功,则请求失败。最后来到视图解析和渲染的执行方法: this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);

    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) {
                this.logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException)exception).getModelAndView();
            } else {
                Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
                mv = this.processHandlerException(request, response, handler, exception);
                errorView = mv != null;
            }
        }
        if (mv != null && !mv.wasCleared()) {
            //这个方法下面有说明
            this.render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        } else if (this.logger.isDebugEnabled()) {
            this.logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this.getServletName() + "': assuming HandlerAdapter completed request handling");
        }
        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
            }
    
        }
    }
    

    它首先判断一下是否异常,如果出现异常,判断异常是否是ModelAndViewDefiningException的实例,如果是就直接调用getModelAndView方法返回一个mv,否则调用processHanlderException方法初始化mv对象;如果没有异常出现,那么判断mv是否为空,为空抛出异常,不为空则判断mv,wasCleared()方法返回的值是否为真;判断完成以后,执行了 this.render(mv, request, response);方法。

    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        Locale locale = this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale();
        response.setLocale(locale);
        String viewName = mv.getViewName();
        View view;
        if (viewName != null) {
            view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);
            if (view == null) {
                throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.getServletName() + "'");
            }
        } else {
            view = mv.getView();
            if (view == null) {
                throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + this.getServletName() + "'");
            }
        }
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'");
        }
    
        try {
            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
            view.render(mv.getModelInternal(), request, response);
        } catch (Exception var8) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'", var8);
            }
    
            throw var8;
        }
    }
    

    它首先设置了视图的编码格式,然后通过 view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);进行了视图解析:

    public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Rendering view with name '" + this.beanName + "' with model " + model + " and static attributes " + this.staticAttributes);
        }
        Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);
        this.prepareResponse(request, response);
        this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);
    }
    

    它首先通过 Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);获取了传入的model的map集合。然后通过 this.prepareResponse(request, response);设置响应头的两个参数,最后通过 this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);将mergeModel合到请求里.

    protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.exposeModelAsRequestAttributes(model, request);
        this.exposeHelpers(request);
        String dispatcherPath = this.prepareForRendering(request, response);
        RequestDispatcher rd = this.getRequestDispatcher(request, dispatcherPath);
        if (rd == null) {
            throw new ServletException("Could not get RequestDispatcher for [" + this.getUrl() + "]: Check that the corresponding file exists within your web application archive!");
        } else {
            if (this.useInclude(request, response)) {
                response.setContentType(this.getContentType());
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Including resource [" + this.getUrl() + "] in InternalResourceView '" + this.getBeanName() + "'");
                }
    
                rd.include(request, response);
            } else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Forwarding to resource [" + this.getUrl() + "] in InternalResourceView '" + this.getBeanName() + "'");
                }
    
                rd.forward(request, response);
            }
    
        }
    }
    

    它首先进行了 this.exposeModelAsRequestAttributes(model, request);操作:

    protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
        model.forEach((modelName, modelValue) -> {
            if (modelValue != null) {
                request.setAttribute(modelName, modelValue);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() + "] to request in view with name '" + this.getBeanName() + "'");
                }
            } else {
                request.removeAttribute(modelName);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Removed model object '" + modelName + "' from request in view with name '" + this.getBeanName() + "'");
                }
            }
    
        });
    }
    

    由此可见,model中的数据,最终是放在request域中的。

    然后他们进行了RequestDispatcher 对象配置,就是选择它要进行的操作了,是使用include还是forward进行跳转。至此,视图的解析与渲染工作都完成了,并且也发送到了前端页面,完成了一个请求的整个流程。

    5.总结

    SpringMVC的总体执行流程的源码解析就为以上描述,我们发现SpringMVC最主要的控制器类DispatcherServlet,它在项目的web.xml中配置拦截地址。当我们发送请求时就可以进入到以上代码中,从而完成整个请求处理。
    

    相关文章

      网友评论

          本文标题:Java面试:SpringMVC工作流程

          本文链接:https://www.haomeiwen.com/subject/vrfhfrtx.html