本文共 12477 字,大约阅读时间需要 41 分钟。
DispatcherServlet是前端控制器设计模式的实现,提供了Spring Web MVC的集中访问点, 而且负责职责的分派,而且与Spring Ioc容器无缝集成, 从而可以获的Spring的所有好处。
作用
DispatcherServlet主要用作职责调度工作,本身主要用于控制流程,主要职责如下:
- 文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析
- 通过HandlerMapping,将请求映射到处理器(返回一个HandlerExecutionChain,它包括一个处理器、多个HandlerInterceptor拦截器)
- 、通过HandlerAdapter支持多种类型的处理器(HandlerExecutionChain中的处理器)
- 通过ViewResolver解析逻辑视图名到具体视图实现
- 本地化解析
- 渲染具体的视图等
- 如果执行过程中遇到异常将交给HandlerExceptionResolver来解析。
DispatcherServlet的工作流程
DospatcherServlet实际上是一个Servlet(它继承HttpServlet)。DispatcherServlet处理的请求必须在同一个web.xml文件里使用url-mapping定义映射。这是标准的J2EE servlet配置。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 { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. 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)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
下面我们分析一下这些代码的意思,先看这句代码:
processedRequest = checkMultipart(request);
检查这个请求是不是文件上传的请求的。我们具体的看一下它是怎么判断是否是文件上传的。
checkMultipart
转换请求到multipart请求,使multipart解析器可用。如果没有解析器被设置,只需使用现有的请求
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException { if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) { if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) { } else if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) instanceof MultipartException) { } else { return this.multipartResolver.resolveMultipart(request); } } return request;}
这里先是判断multipartResolver这个类是不是为空
multipartResolver是需要我们进行配置的,通常配置如下所示:如果没有配置MultipartResolver的话,则认为不是文件上传的请求,如果配置了MultipartResolver的话,调用isMultipart方法验证是不是文件上传的请求,isMultipart方法的内容如下:
public boolean isMultipart(HttpServletRequest request) { return (request != null && ServletFileUpload.isMultipartContent(request));}
在这里我们看到了一个类是:ServletFileUpload文件上传工具包:commons-fileupload中的类。
SpringMVC对文件上传的处理是借助于commons-fileupload包来实现的。我们去isMultipartContent这个方法中看一下:public static final boolean isMultipartContent( HttpServletRequest request) { if (!POST_METHOD.equalsIgnoreCase(request.getMethod())) { return false; } return FileUploadBase.isMultipartContent(new ServletRequestContext(request));}
这里首先判断一下是不是POST请求,如果不是POST请求直接返回false,接着通过isMultipartContent方法来继续验证:
public static final boolean isMultipartContent(RequestContext ctx) { String contentType = ctx.getContentType(); if (contentType == null) { return false; } if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) { return true; } return false;}
如果请求是POST请求,并且请求头中的Context-Type是以multipart/开头的就认为是文件上传的请求。
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException { if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) { if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) { } else if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) instanceof MultipartException) { } else { return this.multipartResolver.resolveMultipart(request); } } return request;}
如果是文件上传请求,则继续判断这个请求是不是已经被转换为MultipartHttpServletRequest类型了。
在Spring-Web这个jar中有一个过滤器org.springframework.web.multipart.support.MultipartFilter 如果在web.xml中配置这个过滤器的话,则会在过滤器中提前判断是不是文件上传的请求,并将请求转换为MultipartHttpServletRequest类型。 这个过滤器中默认使用的MultipartResolver为StandardServletMultipartResolver。 如果不是MultipartHttpServletRequest类型的话,则判断是不是出现异常了。如果上面这两步返回的都是false,则会执行这句:this.multipartResolver.resolveMultipart(request),将请求转换为MultipartHttpServletRequest类型。我们具体看一下:public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException { Assert.notNull(request, "Request must not be null"); if (this.resolveLazily) { return new DefaultMultipartHttpServletRequest(request) { @Override protected void initializeMultipart() { MultipartParsingResult parsingResult = parseRequest(request); setMultipartFiles(parsingResult.getMultipartFiles()); setMultipartParameters(parsingResult.getMultipartParameters()); setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes()); } }; } else { MultipartParsingResult parsingResult = parseRequest(request); return new DefaultMultipartHttpServletRequest(request, parsingResult.getMultipartFiles(), parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes()); }}
在CommonsMultipartResolver中有一个属性叫resolveLazily,这个属性值代表是不是延迟解析文件上传,默认为false。最终返回的是一个DefaultMultipartHttpServletRequest的类。这里有一个重要的方法是:parseRequest,这个方法干的事是解析文件上传请求。它的底层是commons-fileupload那一套,不同的是Spring在获取FileItem之后,又进行了一下封装,封装为便于Spring框架整合。
下面我们接着看这一句话:
multipartRequestParsed = (processedRequest != request);
processedRequest是checkMultipart(request)这个方法返回的值,如果processedRequest和request不相等的话,则认为是文件上传的请求。
getHandler(processedRequest);
- 为此请求返回HandlerExecutionChain。按顺序尝试所有的handler mapping
获取当前请求对应的处理类,在这个处理链中会包含对应的拦截器的信息。HandlerExecutionChain这个类中包含变和不变量的两部分内容
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { for (HandlerMapping hm : this.handlerMappings) { HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null;}
循环handlerMappings,然后获取对应的执行链,只要找到一个对应的执行链就返回
SpringMVC默认加载三个请求处理映射类:
- RequestMappingHandlerMapping
- SimpleUrlHandlerMapping
- BeanNameUrlHandlerMapping
这三个类有一个共同的父类:AbstractHandlerMapping。
hm.getHandler(request)这个getHandler方法在AbstractHandlerMapping中,它的子类都没有重写这个方法。
AbstractHandlerMethodMapping#getHandler
@Overridepublic final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 两个子类重写该方法:AbstractHandlerMethodMapping和AbstractUrlHandlerMapping Object handler = getHandlerInternal(request); // 如果没有找到的话,默认处理类 if (handler == null) { handler = getDefaultHandler(); } // 如果没有默认的处理类,返回null if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } // 包装为执行器链 HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); // 是不是跨域请求 if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain;}
AbstractHandlerMethodMapping#getHandlerInternal
@Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 得到请求 url 的查询路径 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); } // 获取并发读锁 this.mappingRegistry.acquireReadLock(); try { // 得到查询路径对应的处理器方法 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
getHandlerExecutionChain
创建执行器链的内容:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { //判断handler是不是执行器链,如果不是创建一个执行器链 HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); //包装拦截器 for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain;}
这个方法主要是创建执行器链,添加拦截器.
判断
if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return;}
如果没有找到对应的处理类的话,这里通常会返回404,如果throwExceptionIfNoHandlerFound属性值为true的情况下会抛出异常。
我们继续往下分析:HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
getHandlerAdapter
获取处理适配器
- SimpleControllerHandlerAdapter 适配SimpleUrlHandlerMapping和BeanNameUrlHandlerMapping的映射的,也就是实现Controller接口的Handler
- AbstractHandlerMethodAdapter 适配RequestMappingHandlerMapping,也就是我们常用的RequestMapping注解
- HttpRequestHandlerAdapter 适配远程调用的
- SimpleServletHandlerAdapter 适配Servlet实现类的
supports
supportsInternal
总是返回true ,因为任何方法参数和返回值类型会以某种方式加以处理。
不被任何HandlerMethodArgumentResolver识别的方法参数被解释为一个请求参数,如果它是一个简单的类型,或者作为模型属性否则。 没有任何HandlerMethodReturnValueHandler识别的返回值将被解释为一个模型属性 如果是GET请求,内容没有变化则直接返回applyPreHandle
- 应用已注册的preHandle拦截方法
handle()
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
执行handleapplyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);
如果返回的mv不为null,并且没有设置view,则设置默认的view
应用postHandle拦截器。processDispatchResult
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) { mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } //返回的ModelAndView不为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 (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } // 调用处理拦截器的afterCompletion if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, null); }}
如果
- 出现异常,返回异常页面
- 没有异常,ModelAndView不为null,则正常渲染页面,调用拦截器的afterCompletion方法
转载地址:https://javaedge.blog.csdn.net/article/details/106527295 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!