前言
Servlet中的service方法用于应答浏览器请求,每次请求都会调用该方法。DispatcherServlet也是一个Servlet。其继承图如下
一、请求的分发
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
//判断是不是patch请求 或者httpMethod为null
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
//如果不是patch请求,底层也是调用processRequest方法
//因为FrameworkServlet重写了如doPost的方法
super.service(request, response);
}
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
// 记录抛出的异常~~~(若有的话)
Throwable failureCause = null;
//拿到之前的LocaleContext上下文(因为可能在Filter里已经设置过了)
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// 以当前的request创建一个Local的上下文,后面会继续处理
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
// 这里面build逻辑注意:previousAttributes若为null,或者就是ServletRequestAttributes类型,
//那就new ServletRequestAttributes(request, response);
// 若不为null,就保持之前的绑定结果,不再做重复绑定了(尊重原创)
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
// 拿到异步管理器。这里是首次获取,会new WebAsyncManager(),然后放到request的attr里面
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
//这里需要注意:给异步上下文恒定注册了RequestBindingInterceptor这个拦截器(作用:绑定当前的request、response、local等)
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//这句话很明显,就是吧request和Local上下文、RequestContext绑定
initContextHolders(request, localeContext, requestAttributes);
try {
//模版设计模式:由子类DispatcherServlet去实现实际逻辑
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
//这个时候已经全部处理完成,视图已经渲染了
//doService()方法完成后,重置上下文,也就是解绑
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
//日志
logResult(request, response, failureCause, asyncManager);
//关键:不管执行成功与否,都会发布一个ServletRequestHandledEvent事件
//说我处理了这个请求(有需要监听的,就可以监听这个事件了,每次请求都会有)
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
publishRequestHandledEvent()发布请求处理完后的事件源码
private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,
long startTime, @Nullable Throwable failureCause) {
//当publishEvents设置为true和 webApplicationContext 不为空就会处理这个事件的发布
if (this.publishEvents && this.webApplicationContext != null) {
// 计算出处理该请求花费的时间
long processingTime = System.currentTimeMillis() - startTime;
this.webApplicationContext.publishEvent(
//ServletRequestHandledEvent这个事件:目前来说只有这里会发布
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause, response.getStatus()));
}
}
下面我们来写个监听器,专门来监听这个事件:
//专门监听ServletRequestHandledEvent时间的监听器
@Slf4j
@Component
public class ServletReqestHandledEventListener implements ApplicationListener<ServletRequestHandledEvent> {
@Override
public void onApplicationEvent(ServletRequestHandledEvent event) {
//url=[/demowar_war/controller/hello]; client=[127.0.0.1]; method=[GET]; servlet=[dispatcher]; session=[null]; user=[null]; time=[143ms]; status=[OK]
log.info(event.getDescription());
log.info("返回状态码为:" + event.getStatusCode()); //返回状态码为:200
log.info("异常信息为:" + event.getFailureCause()); //异常信息为:null
log.info("处理请求耗时为:" + event.getProcessingTimeMillis()); //处理请求耗时为:143
log.info("事件源为:" + event.getSource()); //事件源为:org.springframework.web.servlet.DispatcherServlet@3e7fadbb
}
}
二、请求的处理
DispatcherServlet#doDispatch方法解析
首先根据请求的路径找到HandlerMethod(带有Method反射属性,也就是对应Controller中的方法),
然后匹配路径对应的拦截器,有了HandlerMethod和拦截器构造个HandlerExecutionChain对象。HandlerExecutionChain对象的获取是通过HandlerMapping接口提供的方法中得到。
有了HandlerExecutionChain之后,通过HandlerAdapter对象进行处理得到ModelAndView对象,HandlerMethod内部handle的时候,使用各种HandlerMethodArgumentResolver实现类处理HandlerMethod的参数(非常重要),使用各种HandlerMethodReturnValueHandler实现类处理返回值。 最终返回值被处理成ModelAndView对象,这期间发生的异常会被HandlerExceptionResolver接口实现类进行处理。
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 如果该请求是include的请求(请求包含) 那么就把request域中的数据保存一份快照版本
// 等doDispatch结束之后,会把这个快照版本的数据覆盖到新的request里面去
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
// 说得很清楚,把一些常用对象放进请求域 方便Handler里面可以随意获取
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); //这个是web子容器哦
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
// 如果是重定向,放置得更多一些~~~~
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
// DispatcherServlet最重要的方法,交给他去分发请求你、找到handler处理等等
doDispatch(request, response);
} finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
//如果是include请求 会上上面的数据快照,重新放置到request里面去
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//此处用processedRequest 需要注意的是:若是处理上传,processedRequest 将和request不再指向同一对象
HttpServletRequest processedRequest = request;
// 异常链处理器
HandlerExecutionChain mappedHandler = null;
//是否文件上传的标志位
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//checkMultipart 这个方法很重要,判断是否是上传需求。且看下面的具体分析:::
//如果请求是POST请求,并且请求头中的Context-Type是以multipart/开头的就认为是文件上传的请求
processedRequest = checkMultipart(request);
// 标记一下:是否是文件上传的request了
multipartRequestParsed = (processedRequest != request);
//解析请求链接,然后根据请求链接找到执行这个请求的类
//(HandlerMapping所说的handler,也就是我们写的Controller或是Action)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
//如果没有找到对应的Handler,返回一个404异常
noHandlerFound(processedRequest, response);
return;
}
// 根据实际的handler去找到一个合适的HandlerAdapter,方法详细逻辑同getHandler,因此不再解释
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 如果是GET请求,如果内容没有变化的话,则直接返回
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;
}
// 利用适配器HandlerAdapter处理当前Handler
// 真正执行我们自己书写的controller方法的逻辑。返回一个ModelAndView
// 这也是一个很复杂的过程(序列化、数据绑定等等),需要后面专题讲解
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 如果异步启动了,这里就先直接返回了,也就不会再执行拦截器PostHandle之类的
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//意思是:如果我们没有设置viewName,就采用默认的。否则采用我们自己的
applyDefaultViewName(processedRequest, mv);
// 执行所有的拦截器的postHandle方法,并且把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()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}