当前位置: 首页 > news >正文

网站的建设时间表/什么是淘宝搜索关键词

网站的建设时间表,什么是淘宝搜索关键词,男女做暖暖的视频试看网站,wordpress tint2.2背景 本文作为 SpringMVC系列 第二篇,介绍HTTP请求的调用链:从请求进入Tomcat到数据流返回客户端的完整过程。为了尽可能把流程表达清楚,进行了很多减支处理,只关注主线逻辑。 本文也作为SpringMVC系列后续文章的基础&#xff0…

背景

本文作为 SpringMVC系列 第二篇,介绍HTTP请求的调用链:从请求进入Tomcat到数据流返回客户端的完整过程。为了尽可能把流程表达清楚,进行了很多减支处理,只关注主线逻辑。

本文也作为SpringMVC系列后续文章的基础,在调用链梳理清楚的基础上,后文对重要逻辑分别进行展开介绍,如拦截器、异常处理器、转换器、消息转换器、异步请求、文件上传等。在这些文章完成后,会出一个Spring框架应用专题,包括:结果集框架、错误码框架、鉴权逻辑、分页查询、事件框架等,基于此会对Spring系列和SpringMVC系列文章有更深层次的理解。

1.调用链

Tomcat从逻辑上可以分为连接器(coyote)和Servlet容器(catalina)两个部分:coyote负责接收客户端的请求,并按照协议对请求进行解析,封装成Java对象后发送给catalina以及将catalina返回的消息推送给客户端;catalina提供了Servlet容器实现,负责处理具体的请求并进行响应。
其中,coyote封装了底层的网络通讯(Socket),为catalina提供了统一的接口(Request/Response对象)而与Servlet容器解耦;catalina内部通过适配器将(Request/Response对象)转换为(HttpRequest/HttpResponse对象),然后将消息发送给Servlet对象,流程图如下所示:
在这里插入图片描述
总之,当Http请求到达Tomcat连接池后,会将请求消息封装成(HttpRequest/HttpResponse对象), 通过调用Servlet标准接口实现消息的传递。
SpringMVC框架对应的Servlet对象为DispatcherServlet,即调用栈会进入DispatcherServlet的void service(ServletRequest req, ServletResponse res)方法。
因此,有必要了解一下DispatcherServlet类的继承关系以及对Servlet方法实现情况,如下图所示:
![在这里

2.HttpServlet

在这里插入图片描述

DispatcherServlet关于Servlet:void service(ServletRequest req, ServletResponse res)接口的实现逻辑在HttpServlet类中,代码如下:

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {HttpServletRequest  request;HttpServletResponse response;try {request = (HttpServletRequest) req;response = (HttpServletResponse) res;} catch (ClassCastException e) {throw new ServletException(lStrings.getString("http.non_http"));}service(request, response);}

逻辑较为简单,直接将ServletRequest/ServletResponse对象转为HttpServletRequest/HttpServletResponse,并调用service(HttpServletRequest req, HttpServletResponse resp)接口。后者中根据HTTP方法类型派发给了doGet/doPost/doPut等接口;而doGet/doPost/doPut等接口的实现逻辑在FrameworkServlet中归一到void processRequest(HttpServletRequest request, HttpServletResponse response)接口中:

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {processRequest(request, response);
}@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {processRequest(request, response);
}@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {processRequest(request, response);
}

总之,所有来自Servlet的请求都会进入processRequest方法中进行处理。

3.FrameworkServlet

在这里插入图片描述

processRequest方法的主线逻辑如下:

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();LocaleContext localeContext = buildLocaleContext(request);RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());initContextHolders(request, localeContext, requestAttributes);try {doService(request, response);} finally {resetContextHolders(request, previousLocaleContext, previousAttributes);}
}

上述代码在逻辑上可以分为三块:调用doService(request, response)接口处理并响应、调用前的准备工作、调用后的清理工作。
doService接口作为主体逻辑在下一节中进行介绍,本节对准备工作和清理工作进行介绍。

准备工作

LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);// WebAsyncManager提供了HTTP请求异步处理能力,不是本文的重点
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

根据request对象获取Locale对象并将其通过ThreadLocal对象中,将request和response对象封装在RequestAttributes对象中,保存在ThreadLocal对象中;借助ThreadLocal的能力,对外提供了静态方法,使得程序在任意时刻都取得Locale对象、request和response对象。

清理工作

清理工作对应从内存中将该线程相关的Locale对象和request和response对象从内存中清除,以防止内存泄露。

resetContextHolders(request, previousLocaleContext, previousAttributes);private void resetContextHolders(HttpServletRequest request, LocaleContext prevLocaleContext, RequestAttributes previousAttributes) {LocaleContextHolder.setLocaleContext(prevLocaleContext, this.threadContextInheritable);RequestContextHolder.setRequestAttributes(previousAttributes, this.threadContextInheritable);
}

这是一个使用ThreadLocal基本的套路,后面介绍Spring应用专题时会涉及。

4.DispatchServlet

在这里插入图片描述

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {// springMVC容器request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());// locale解析器request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);// 主体相关request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());// flashMapManager在后续介绍转发和重定向内容时进行介绍,这里暂时忽略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 {doDispatch(request, response);} finally {// 清理工作 
}

doService逻辑比较简单,包括:调用doDispatch方法前对request的属性设置,以及在调用之后清理工作。doDispatch方法是核心,其他逻辑是在为调用该方法做的准备或收尾工作。
本文希望将HTTP调用链的主线逻辑表达清楚,因此会省去异步请求(WebAsyncManager相关)、文件上传(MultipartResolver相关)、异常捕获等逻辑,如下所示:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;try {ModelAndView mv = null;Exception dispatchException = null;try {mappedHandler = getHandler(request);if (mappedHandler == null) {noHandlerFound(request, response);return;}HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());if (!mappedHandler.applyPreHandle(request, response)) {return;}mv = ha.handle(request, response, mappedHandler.getHandler());mappedHandler.applyPostHandle(request, response, mv);} catch (Exception ex) {dispatchException = ex;}processDispatchResult(request, response, mappedHandler, mv, dispatchException);} catch (Exception ex) {triggerAfterCompletion(request, response, mappedHandler, ex);} 
}

上述逻辑可以表示为:
【插图】

拦截器的preHandle方法按照拦截器在列表中的顺序正向执行,postHandle和afterCompletion反向执行。

上图可以分为以下几个步骤:
(1)根据request对象获取执行链HandlerExecutionChain;执行链由Interceptor(拦截器)和Handler(对controller的包装)组成;
(2)根据Handler获取HandlerAdapter;
(3)依次调用拦截器的preHandle方法;
(4)借助HandlerAdapter 通过反射调用目标方法(controller对应的方法);
(5)依次调用拦截器的postHandle方法;
(6)对结果或者异常进行后置处理;
(7)依次调用拦截器的afterCompletion方法;

上述步骤为正常执行流程,异常场景包括:

异常场景1:
当根据equest对象获取执行链HandlerExecutionChain失败时(没有匹配项),则直接想客户端返回404。

异常场景2:
调用某个拦截器的preHandle方法返回false时,则反向执行已执行过拦截器的afterCompletion方法,并跳出doDispatch方法。
在这里插入图片描述
涉及的代码逻辑如下:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {//...if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}//...
}boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for (int i = 0; i < this.interceptorList.size(); i++) {HandlerInterceptor interceptor = this.interceptorList.get(i);if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}return true;
}

当有拦截器的preHandle方法返回false时,调用triggerAfterCompletion(request, response, null)方法:

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);try {interceptor.afterCompletion(request, response, this.handler, ex);} catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}
}

倒序执行已执行过preHandle的拦截器。
异常场景3:
preHandle或者反射调用目标方法(Controller接口)过程中有异常抛出时,经过异常捕获将异常包装成Exception对象,并通过processDispatchResult方法处理结果:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {try {//。。。} catch (Exception ex) {dispatchException = ex;} catch (Throwable err) {dispatchException = new NestedServletException("Handler dispatch failed", err);}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}

processDispatchResult的主线逻辑如下:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {if (exception != null) {mv = processHandlerException(request, response, mappedHandler.getHandler(), exception);}mappedHandler.triggerAfterCompletion(request, response, null);
}

如果有异常抛出,则exception不为空,通过processHandlerException方法处理异常场景。
如论是否有异常抛出,最后都通过mappedHandler.triggerAfterCompletion会触发拦截的afterCompletion方法。
同样需要注意:拦截器在执行preHandle过程中抛出的异常,则会反向执行已执行过preHandle方法拦截器的afterCompletion方法。
如果在拦截器的postHandle或者反射调用目标方法过程中抛出的异常,则会执行所有拦截器的afterCompletion方法,流程如下所示:
在这里插入图片描述

http://www.jmfq.cn/news/4852801.html

相关文章:

  • 江苏病毒感染最新消息/seo课程多少钱
  • 北京师范大学学风建设专题网站/友情链接交换的作用在于
  • 烟台做网站的公司/农技推广
  • 做网站用后缀好/360开户推广
  • 网站建设的工作在哪里找客户资源/重庆网站快速排名优化
  • 功能网站开发多少钱/日本和韩国是亚洲的国家
  • 万网网站后台留言在哪/公司怎么做网站推广
  • 百度搜索不到我的网站/网络营销的好处
  • 网站建设应考虑哪些方面的问题/百度推广费用怎么算
  • 沈阳手机网站建设/宁德市高中阶段招生信息平台
  • php动态网站建设内容/seo外包网络公司
  • 自己怎样做优惠券网站/汕头搜索引擎优化服务
  • 青岛企业级网站设计/网络工具
  • 泰安网络犯罪案件/昆明自动seo
  • 慧聪网郭凡生/宁波seo排名方案优化公司
  • 网站怎么建设在哪里接单/免费广告投放平台
  • 网站快照优化怎么做/怎么宣传网站
  • 上海那家公司做响应式网站建设/seo资料网
  • 英文医疗网站建设/推广免费
  • 怎么在百度做原创视频网站/制作网页用什么软件
  • 学室内设计网站/百度如何做推广
  • 网站怎么做?/有什么好的网站吗
  • qq免费的推广引流软件/seo案例分析及解析
  • 有哪些网站做美食的图片很精致/网站外链发布平台
  • 做个网站怎么做/深圳seo外包公司
  • 网站建设销售技巧和话术/百度网站流量查询
  • 惠州网/百度手机端排名如何优化
  • 小程序申请流程/开封seo公司
  • 手机移动端网站做多大/暴风seo论坛
  • 微商/百度上做优化