前言:SpringMVC的面试题常见的也就那几种,本文我打算分为两个方向为大家介绍SpringMVC的面试题。
第一部分将从源码的执行的角度分析SpringMVC(以后简称MVC)
第二部分将从面试官常问的SpringMVC面试题取介绍
流程大致分析一下:首先会请求会进入前端控制器的doService方法->调用doDispatch方法->调用handle方法->这个handle方法需要从RequestMappingHandlerAdapter的父类找到AbstractHandlerMethodAdapter里面的handle方法,这个方法又调用RequestMappingHandlerAdapter的handleInternal方法->调用this的invokeHandlerMethod->invokeAndHandle->invokeForRequest->doInvoke方法->doInvoke里面调用this.getBridgedMethod().invoke其中this.getBridgedMethod()这个就是controller里面的那个方法,这就利用了反射机制,这就是MVC的执行流程,期间经历了RequestMappingHandlerAdapter这个处理器适配器,ServletInvocableHandlerMethod Servlet处理控制方法,InvocableHandlerMethod执行方法反射这主要的三个类
下面将用截图代码分析MVC的执行流程
首先进入前端控制器的doservice方法,调用doDispatch方法
xa
下面看下doDispatch方法干了啥事吧
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对象 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); } } }下面我们来看下mv = ha.handle(processedRequest, response, mappedHandler.getHandler());做了什么事吧
说白了,这就是调用了RequestMappingHandlerAdapter里面的handleInternal方法
可以看下invokeForRequest方法,最后调用doInvok方法
第二种介绍:springmvc的流程图分析:
1.用户请求进入前端控制器,调用doservice在调用doDispatch方法,调用getHandler方法,返回一个HandlerExecutionChain,里面包含一个你自己写的controller和mvc的拦截器,获得这个执行链mappedHandler之后,调用getHandlerAdapter(mappedHandler.getHandler())方法,获取适配器,这就是上图右半部分的分析,左半部分暂时未做分析
代码截图分析:
不要意思,纠正下错误,默认提供的映射器只有两种
拿到handler,来调用适配器的代码分析如下:
2.首先我们需要知道的是,我们写在controller里面的请求路径可是在应用程序加载启动时就被MVC加载好了的,存放到了一个Map里面。我们来分析下源码:
2.1在前端控制器中有一个initHandlerMappings方法,我们在这个方法打一个断点,程序在启动的时候就会进来
2.2我们debug走到我们打断点的地方
点开这些内容:
我们会发现,MVC已经把对对应的关系放到了mappLookup中了,后续只需要取这个map里面取就行
3.MVC参数封装的原理分析(以最基础的类型参数封装为例)
前面的请求啥的都和前面分析的一样,我们直接看ServletInvocableHandlerMethod类里面的invoForRequest方法
进去getMethodArgumentValues方法里面发现,主要是有一个argumentResove在起作用
进入HandlerMethodArgumentResolverComposite类里面
进入RequestParamMethodArgumentResolver这个类,通过servlet原始的封装getParamtermValues获取参数的值