前言:SpringMVC的面试题常见的也就那几种,本文我打算分为两个方向为大家介绍SpringMVC的面试题。
第一部分将从源码的执行的角度分析SpringMVC(以后简称MVC)
第二部分将从面试官常问的SpringMVC面试题取介绍
SpringMVC源码介绍
1.http://localhost:8000/hello这个路径的执行流程是怎么走的
流程大致分析一下:首先会请求会进入前端控制器的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.映射路径的初始化源码分析
2.首先我们需要知道的是,我们写在controller里面的请求路径可是在应用程序加载启动时就被MVC加载好了的,存放到了一个Map里面。我们来分析下源码:
2.1在前端控制器中有一个initHandlerMappings方法,我们在这个方法打一个断点,程序在启动的时候就会进来
2.2我们debug走到我们打断点的地方
点开这些内容:
我们会发现,MVC已经把对对应的关系放到了mappLookup中了,后续只需要取这个map里面取就行
3.MVC参数封装的原理分析(以最基础的类型参数封装为例)
前面的请求啥的都和前面分析的一样,我们直接看ServletInvocableHandlerMethod类里面的invoForRequest方法
进去getMethodArgumentValues方法里面发现,主要是有一个argumentResove在起作用
进入HandlerMethodArgumentResolverComposite类里面
进入RequestParamMethodArgumentResolver这个类,通过servlet原始的封装getParamtermValues获取参数的值