HttpServletBean主要参与了创建工作,并没有涉及请求的处理。
在FrameworkServlet中重写了service,doGet,doPost(除了doHead以外的所有请求处理方法)
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//得到是何种方法
String method = request.getMethod();
if(method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
this.processRequest(request, response);
} else {
super.service(request, response);
}
}
protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.processRequest(request, response);
}
在这里跟HttpServlet不同,将不同类型的请求路由到不同方法进行处理的思路正好相反,这里又将所有的请求合并到了processRequest方法。
接下来看processRequest方法
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Object failureCause = null;
//获取LocaleContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//获取当前请求的LocaleContext
LocaleContext localeContext = this.buildLocaleContext(request);
//获取RequestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//获取当前请求的ServletRequestAttributes
ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));
//把当前请求的LocaleContext和ServletRequestAttributes设置到LocaleContextHolder和RequestContextHolder
this.initContextHolders(request, localeContext, requestAttributes);
try {
//实际处理
this.doService(request, response);
} catch (ServletException var17) {
failureCause = var17;
throw var17;
} catch (IOException var18) {
failureCause = var18;
throw var18;
} catch (Throwable var19) {
failureCause = var19;
throw new NestedServletException("Request processing failed", var19);
} 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");
}
}
//发布了ServletRequestHanledEvent消息。
this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
}
}
在这个方法之中doService是核心方法,在DispatcherServlet中具体实现。doService前后还做了写事情,装修者模式。主要是做了两件事:
1.对LocaleContext(本地消息)和RequestAttributes(属性获取用于管理request和session)的设置及恢复;
2.处理完后发布了ServletRequestHandledEvent消息。
经过上面的分析我们的代码在doService()中,接下来分析
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if(this.logger.isDebugEnabled()) {
String attributesSnapshot = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult()?" resumed":"";
this.logger.debug("DispatcherServlet with name \'" + this.getServletName() + "\'" + attributesSnapshot + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
HashMap attributesSnapshot1 = null;
if(WebUtils.isIncludeRequest(request)) {
attributesSnapshot1 = new HashMap();
Enumeration inputFlashMap = request.getAttributeNames();
while(inputFlashMap.hasMoreElements()) {
String attrName = (String)inputFlashMap.nextElement();
if(this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot1.put(attrName, request.getAttribute(attrName));
}
}
}
//对request设置属性
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());
FlashMap inputFlashMap1 = this.flashMapManager.retrieveAndUpdate(request, response);
if(inputFlashMap1 != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap1));
}
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() && attributesSnapshot1 != null) {
this.restoreAttributesAfterInclude(request, attributesSnapshot1);
}
}
}
我们首先会设置webApplicationContext,localResolver,themeResolver和themeSource在之后介绍的handler和view中需要使用,到时候再作分析。后面三个属性都和flashMap有关,主要是用于重定向的参数和传递。一般我们重定向的时候传参数过去只有靠url的重写,但是这里我们可以使用flushMap向其中传递我们需要传递的参数然后就可以进行重定向的传参了。
接下来对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 err = null;
Exception dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
if(mappedHandler == null || mappedHandler.getHandler() == null) {
this.noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ex = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if(isGet || "HEAD".equals(method)) {
long lastModified = ex.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;
}
err = ex.handle(processedRequest, response, mappedHandler.getHandler());
if(asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(request, err);
mappedHandler.applyPostHandle(processedRequest, response, err);
} catch (Exception var19) {
dispatchException = var19;
}
this.processDispatchResult(processedRequest, response, mappedHandler, err, dispatchException);
} catch (Exception var20) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var20);
} catch (Error var21) {
this.triggerAfterCompletionWithError(processedRequest, response, mappedHandler, var21);
}
} finally {
if(asyncManager.isConcurrentHandlingStarted()) {
if(mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if(multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
上面是大概的代码其实把所有代码精简了之后缩略为4句:
1.mappedHandler = this.getHandler(processedRequest);
2.HandlerAdapter ex = this.getHandlerAdapter(mappedHandler.getHandler());
3.err = ex.handle(processedRequest, response, mappedHandler.getHandler());
4.this.processDispatchResult(processedRequest, response, mappedHandler, err, dispatchException);
上面4句话概括为:
1.根据request找到Handler。
2.根据Handler找到对应的HandlerAdapter
3.用HandlerAdapter处理Handler
4.调用processDispatchResult处理方法结果。
这里需要解释三个概念:HandlerMapping,Handler和HandlerAdapter。
Handler:也就是处理器,它直接对应着MVC中的C层也就是controller层,它的具体表现形式有很多,可以是类,也可以是方法,如果你能想到别的表现形式也可以使用。类型是Object。对于注解中我们只要标注了@RequestMapping的所有方法都可以看成一个Handler。只要可以处理实际请求就可以是Handler。
HandlerMapping:用来查找Handler的,在Spring MVC中会处理很多请求,每个请求都需要一个Handler处理,我们接收到请求以后使用哪个Handler来处理呢?这就是HandlerMapping需要来告诉我的。
HandlerAdapter:很多人对这个理解都比较模糊,其实从名字上可以看出他是一个Adapter,也就是适配器。因为Spring MVC中的Handler可以是任意形式,只要能处理请求就OK,但是Servlet需要的处理方法的结构是固定的,都是以request和response为参数,这个时候就需要一个适配器来处理这个Handler。
doDispatch总共可以分成4句话,我们也可以把它分为两部分:处理请求和渲染页面。
在整个程序流程开始的时候我们会有如下的定义
//请求,如果不是上传请求则直接使用接收到的request,否则进行处理
HttpServletRequest processedRequest = request;
//处理请求链,包含我们的拦截器和我们处理器。
HandlerExecutionChain mappedHandler = null;
//上传文件标志位
boolean multipartRequestParsed = false;
//封装Model和View
ModelAndView err = null;
//处理请求过程中抛出的异常
Exception dispatchException = null;
具体步骤
1。doDispatch中首先检查是不是上传请求判断是否是Post并且content-Type是否以”multipart/”开头,如果是则会进行处理。会把文件和对应的名字封装成map。
2。通过getHandler方法获取Handler处理器链,原理是使用HandlerMapping。举一个例子来说:对于我们的方法处理器Mapping来说 会通过我们的AbstractHandlerMethodMapping首先通过我们的URL找到了我们的匹配条件,然后根据我们的匹配条件这里的匹配条件不止是URL匹配这么简单也包括我们get,post方法请求和请求参数等。利用匹配条件找到了Handler。
3。然后使用HandlerAdapter适配器处理我们的请求。
4。处理异常,渲染页面。