您的位置:首页 > 编程语言 > Java开发

SpringMVC源码情操陶冶-DispatcherServlet简析(二)

2017-06-04 14:59 417 查看

承接前文SpringMVC源码情操陶冶-DispatcherServlet类简析(一),主要讲述初始化的操作,本文将简单介绍springmvc如何处理请求


DispatcherServlet#doDispatch()

DispatcherServlet
复写父类的
doService()
方法,其中最主要的处理客户端发的请求便是
doDispath()
方法,我们只深究此方法,大致上看下其中的逻辑

注释瞧一发

/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/

由官方注释中可得出以下结论:



所有的关于
http
协议的方法都是通过本方法来处理

处理过程中,
handler
处理器是核心,优先是从
HandlerMappings
中获取,再而可通过
HandlerAdapter
适配器来再一层包装供支持更多形式的请求


源码简析

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//这里的processedRequest主要用于文件上传类的请求
HttpServletRequest processedRequest = request;
//mappedHandler是处理的核心,此处可以理解为处理链
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {
//视图对象
ModelAndView mv = null;
//异常对象
Exception dispatchException = null;

try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// Determine handler for the current request.优先从HandlerMappings中获取处理器对象
mappedHandler = getHandler(processedRequest);
//这里找不到handler则会出现我们熟悉的"No mapping found"日志打印
if (mappedHandler == null || mappedHandler.getHandler() == null) {
//返回404错误
noHandlerFound(processedRequest, response);
return;
}

//HandlerAdapter必须拥有,否则会抛异常
//最终通过此对象来获取视图对象
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
//获取上次修改事时间,第一次访问为-1L
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
//对未修改的资源get请求,直接返回302状态码
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//拦截器处理请求,调用拦截接口preHandle方法,一旦HandlerInteceptor返回false,则表示拦截成功
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// Actually invoke the handler.通过HandlerAdapter处理请求返回逻辑试图
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

//异步请求直接返回
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//倘若mv对象内部没有逻辑视图名则采取默认视图
applyDefaultViewName(processedRequest, mv);
//调用拦截器接口的postHandle()接口
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
//处理过程中出现了异常
dispatchException = ex;
}
//再次处理,如果有异常出现则需要处理异常信息,因为异常也可有对应的视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//processDispatchResult()出现异常时调用
//倒序调用之前已调用过的HandlerInteceptor接口的afterCompletion()方法,再直接返回异常信息给客户端
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
//processDispatchResult()出现异常时调用
//倒序调用之前已调用过的HandlerInteceptor接口的afterCompletion()方法,再直接返回异常信息给客户端
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
//调用所有的实现了AsyncHandlerInterceptor接口的afterConcurrentHandlingStarted()方法
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
//清除文件上传请求的相关信息,释放资源
cleanupMultipart(processedRequest);
}
}
}
}


基本操作逻辑为:

根据请求路径查找是否存在某个
HandlerMapping
与之对应

根据
HandlerMapping
获取
HandlerExecutionChain
处理链,内部包含一系列的拦截器以及真实处理的
Handler
对象

对处理链返回的Handler或者其本身也为空,则直接返回404错误

根据
HandlerExecutionChain
处理链中的
Handler
对象获取
HandlerAdapter
适配器,如果没有找到则直接返回异常

对GET/HEAD请求做
Last-Modified
校验,如果是第二次重复请求则直接返回302状态

使用
HandlerExecutionChain
处理链中的
interceptors
拦截器依次调用
preHandler()
预拦截方法,如果拦截器执行过程中出现
return false
的情况,则直接被拦截返回

通过
HandlerAdapter
适配器的
handle()
方法返回视图对象

使用
HandlerExecutionChain
处理链中的
interceptors
拦截器依次调用
postHandler()
方法,对返回内容进行补充

对视图对象进行渲染返回

如果在处理过程中出现了异常,则对异常进行相应的抛出或者异常视图的渲染即可能直接返回粗暴的异常信息或者友好的错误信息页面


DispatcherServlet#getHandler()-获取HandlerExecutionChain处理链

简单代码如下

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
//根据HandlerMapping对象获取处理链
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}

主要是注意此处的
handlerMappings
属性,根据我们在springmvc配置中常用
mvc:annotation-driven
节点,我们可以得知包含的属性主要有

RequestMappingHandlerMapping 主要解析
@Controller
注解类并解析其中包含
@RequestMapping
的注解方法包装为
HandlerMethod
作为
handler
对象

BeanNameUrlHandlerMapping 对含有
/
开头的beanName注册为
handler
,
handler
对象则为其本身在springmvc上下文中对应的class类实例

SimpleUrlHandlerMapping 即采用
urlMap
保存路径与处理类的关系,即可以直接指定url对应
handler
对象,其中
handler
对象多为beanName对应的class类【此处不包含】


具体
HanlderMapping
获取处理链对象是通过抽象类
AbstractHandlerMapping#getHandler()
来操作的,限于篇幅过长,遂独立成篇>>>SpringMVC源码情操陶冶-AbstractHandlerMapping


DispatcherServlet#getHandlerAdapter()-获取Handler适配器

获取适配器的目的是为了通过其获取视图对象

//参数handler一般为具体对象
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
//此处的handler可为HandlerMethod/HttpRequestHandler/Controller/Servlet
for (HandlerAdapter ha : this.handlerAdapters) {
//supports方法代表其支持何种handler对象,也就是适配器的意义所在
if (ha.supports(handler)) {
return ha;
}
}
//此处可知当HandlerAdapter没有找到则会抛出ServletException异常
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

HandlerExecutionChain#applyPreHandle()-对请求进行拦截的预处理

调用对应请求路径的拦截器集合的统一方法,源码奉上

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
//获取内部的HandlerInterceptor集合
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//依次执行
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
//调用拦截器的preHandle()方法,一旦返回false则提前结束请求
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}



执行对应请求路径的拦截器集合的统一方法
preHandler()
方法,拦截器一般都是
HandlerInterceptor
接口的实现类

一旦顺序执行过程中,出现
preHandler()
方法中返回false,则表示直接返回,true代表往下执行下一个拦截器


HandlerAdapter#handle()-对请求进行业务处理并返回视图对象

限于篇幅过长,具体可见>>>SpringMVC源码情操陶冶-HandlerAdapter适配器简析

DispatcherServlet#processDispatchResult()-解析视图对象返回结果

主要处理视图和异常视图,具体源码奉上

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

boolean errorView = false;
//对存在异常的优先处理
if (exception != null) {
//是否为ModelAndViewDefinitionException异常
if (exception instanceof ModelAndViewDefiningException) {
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);
//针对异常的视图,清除request对象中的error属性
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}

if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}

//执行拦截器中的afterCompletion()方法
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}

由以上代码可知springmvc优先对异常作视图解析,然后通过
render()
方法渲染视图返回给前台。这其中也会调用拦截器的
afterCompletion()
方法来收尾

DispatcherServlet#processHandlerException()-处理异常返回ModelAndView

通过已注册的
HandlerExceptionResolver
集合来解析异常返回视图,源码奉上

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {

// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
//遍历异常类解析器集合,解析成功则返回
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
//解析器解析异常,查找是否有配备的异常视图
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
if (exMv != null) {
//判断model和view是否为空
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
//尝试设置默认的view对象,默认操作为
//比如"/view/req"-->设置"view/req"为viewName
if (!exMv.hasView()) {
exMv.setViewName(getDefaultViewName(request));
}
//将异常信息保存至request对象中
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
//如果解析器没有解析到合适的视图对象则直接抛出异常
throw ex;
}



HandlerExceptionResolver
对象处理异常信息的解析,主要查找是否有具体的视图绑定到该异常,具体可查看>>>SpringMVC源码情操陶冶-AbstractHandlerExceptionResolver

当查找到的
ModelAndView
对象不含有view对象但含有model对象时,其会尝试获取默认视图,比如请求路径为"/view/req"-->设置"view/req"为viewName

当查找不到
ModelAndView
对象时则直接返回异常,这将导致前端页面会展示具体的异常信息


DispatcherServlet#render()-渲染视图

渲染视图返回给前端页面,具体源码奉上

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);

View view;
//判断mv内部属性view是否为string类型
if (mv.isReference()) {
// 根据mv通过viewResolvers集合(FreemarkerResolver/VelocityResolver..)解析获取视图对象
// 包括寻找页面
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
//表明存储在ModelAndView对象内部的view要么是String.class类型要么是View.class类型
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
try {
//最后才是对视图的渲染,返回具体页面给前台
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
//渲染失败仍会抛出异常直接返回给前端
throw ex;
}
}



ModelAndView对象中的内部属性
view
要么是String.class类型要么是View.class接口类型,两者选其一

针对
view
类型为String.class的,通过springmvc上下文已注册的
ViewResolver
集合,调用其统一一接口方法
resolveViewName()
方法来获取View对象。具体>>>SpringMVC源码情操陶冶-ViewResolver视图解析

最后渲染视图,并将model等属性绑定到页面引擎中,比如freemarker/groovy等,具体>>>SpringMVC源码情操陶冶-View视图渲染

当然渲染失败,也会直接将异常显示给前端页面


小结


本文只对springmvc如何处理请求并返回作下简单的简析

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: