疑问的产生

这个疑问是我在写文件下载的时候产生的,我是用HttpServletResponse获取到Outputstream,然后利用OutputStream直接写数据的。当时我就想这个OutputStream是不是就是对应的Socket连接的OutputStream。即是不是的程序在用stream写的时候,数据也同时在发?

Response的OutputStream把数据写到哪去?

于是我看了下HttpServletResponse的getOutputStream方法,看看它注释是怎么说的。

    /**
* Returns a {@link ServletOutputStream} suitable for writing binary
* data in the response. The servlet container does not encode the
* binary data.
*
* <p> Calling flush() on the ServletOutputStream commits the response.
*
* Either this method or {@link #getWriter} may
* be called to write the body, not both, except when {@link #reset}
* has been called.
*
* @return a {@link ServletOutputStream} for writing binary data
*
* @exception IllegalStateException if the <code>getWriter</code> method
* has been called on this response
*
* @exception IOException if an input or output exception occurred
*
* @see #getWriter
* @see #reset
*/
public ServletOutputStream getOutputStream() throws IOException;

以上,注释有说明是OutputStream是用来写响应body内容的,也有提到flush()方法,说明肯定是有缓冲的,所以应该不是直接操作socket写数据。我猜测应该是有一个字节数组用来暂时存储,然后统一flush。但是还是不太确定,于是简单翻阅了下tomcat源码。

找到ServletOutputStream的实现类CoyoteOuputStream。它实现了OutputStream的抽象方法write,把数据写入到OutputBuffer类型的字段中存着。而这个OutputBuffer对象来自于coyote/Response。其实这个OutputBuffer也只是一个接口,具体实现一直向下翻是StreamOutputBuffer。数据大小没有限制,是用链表存储的,每个链表节点存储8196字节。

什么时候把响应数据报返回给客户端?

其实就是查看,它是何时调用OutputBuffer的flush方法的。我逐层查看,最终定位到了connector/Response的finishResponse()方法。这个方法,会先发送响应行和响应头。然后再发送响应body。Tomcat的源码我看的不多,这里找到一张不错的时序图,描述的是一个HTTP请求的处理过程。如下,我们把重点放在servlet的service方法调用,和Response的fininshResponse方法调用上。可以得到,在service方法返回后,执行的就是finishResponse操作。也就是说,当servlet程序处理完这个请求后,tomcat就会把响应结果发回客户端

注意:servlet的程序不参与底层数据的收发,或者说不控制

servlet的service方法调用在图中哪里?

包含在ApplicationFilterChain的internalDoFilter方法中。

servlet程序处理请求指的是什么?

根本上servlet程序做的工作就是,根据Request的信息,填充Response信息而已。

servlet程序与Spring MVC是什么关系?

Spring MVC底层还是Serlvet,它是把所有请求都用一个servlet处理,这个servlet叫做DispatcherServlet,而它又把请求分发给对应的@RequestMapping标注的方法进行处理。整体上来说就是完成一个service方法的调用。

那MVC的返回页面,返回REST数据是怎么回事?

返回页面就是把页面数据写入到响应Body中;@ResponseBody注解,实际上就是把@RequestMapping标注的方法的返回值转为JSON字符串写入到响应Body中。这里的响应Body指的就是前文中的OutputBuffer.

Tomcat与Servlet程序的职责

《How Tomcat works》中讲到,Servlet容器(Tomcat就是一种Servlet容器)的任务有概括地讲有三个

1.创建一个Request对象,并填充相关信息(parameters、headers、cookie、uri等)

2.创建一个Response对象

3.调用与此请求关联的Servlet的service方法,把Request和Response传给它。

这里我用自己的话讲一下:当浏览器向服务端发来一个请求时,服务端会将请求数据报的内容解析出来,创建一个填充有请求信息的Request对象,同时创建一个"空的"Response对象,然后把这两个对象传给servlet的service方法,让它来完成Response对象的填充,最后把Response数据发送给客户端。

为什么要传Request对象?

你不传Request对象,Servlet程序就不知道该填充什么。换句话说,它不知道你到底想要什么资源。

Tomcat是如何找到请求关联的Servlet的?

我们知道,Tomcat在开发的时候不可能知道你会往它里面部署什么项目,servlet程序叫什么。所以它不可能硬编码来调用service方法,它所使用的就是反射机制。

想想在使用spring boot框架开发之前,我们是怎么部署项目的?就是把项目打包,然后放到Tomcat的webapp目录下。跑起来后,项目对应的URL就是localhost:8080/projectName/xxx这样是吧。而且,在项目中,不管是注解式的,还是web.xml式,都会配置Servlet程序的映射。把URL映射到某个Servlet类文件。

当请求来临时,先根据projectName找到对应项目,再根据后续的URL映射到对应的Servlet类名。之后Tomcat就会利用反射机制加载Servlet类文件,获取实例,然后再调用service方法。

coyote/Response、connector/Response、connector/ResponseFacade之间的关系?

coyote/Response主要就是跟底层的数据传递挂钩的,而connector/Response是coyote/Response的上层包装,它实现了HttpServletResponse接口。但是如果将它直接传给service方法,则害怕用户直接将HttpServletResponse强转为connector/Response,直接调用底层的一些方法。所以引入了一个使用"Facade模式",将connector/Response除了HttpServletResponse接口定义的public方法都屏蔽掉。也就是说,传递给service的实际上是connector/ResponseFacade对象,就算强转为实际类型,也只能看到HttpServletResponse接口定义的方法。

【杂谈】tocmat是何时写回响应数据报的的更多相关文章

  1. 基于netty http协议栈的轻量级流程控制组件的实现

    今儿个是冬至,所谓“冬大过年”,公司也应景五点钟就放大伙儿回家吃饺子喝羊肉汤了,而我本着极高的职业素养依然坚持留在公司(实则因为没饺子吃没羊肉汤喝,只能呆公司吃食堂……).趁着这一个多小时的时间,想跟 ...

  2. SpingMVC 核心技术帮助文档

    声明:本篇文档主要是用于参考帮助文档,没有实例,但几乎包含了SpringMVC 4.2版本的所有核心技术,当前最新版本是4.3,4.2的版本已经经是很新的了,所以非常值得大家一读,对于读完这篇文档感觉 ...

  3. Android 学习笔记之Volley开源框架解析(五)

    学习内容: 1.PoolingByteArrayOutputStream 2.ByteArrayPool 3.HttpStack 4.HurlStack 5.HttpHeaderParser   前面 ...

  4. Android 学习笔记之Volley开源框架解析(四)

    学习内容: 1.NetWorkDispatcher网络请求线程调度... 2.NetWork网络请求抽象类... 3.BasicNetWork网络请求抽象类的具体实现... 4.NetWorkResp ...

  5. Android 学习笔记之Volley开源框架解析(三)

      学习内容: 1.CacheDispatcher缓存请求调度... 2.Cache缓存数据的保存... 3.DiskBasedCache基于磁盘的缓存类实现方式...   前面说到使用Volley发 ...

  6. DNS安全浅议、域名A记录(ANAME),MX记录,CNAME记录

    相关学习资料 http://baike.baidu.com/link?url=77B3BYIuVsB3MpK1nOQXI-JbS-AP5MvREzSnnedU7F9_G8l_Kvbkt_O2gKqFw ...

  7. DNS安全浅议、域名A记录(ANAME),MX记录,CNAME记录(转)

    http://www.cnblogs.com/LittleHann/p/3828927.html 相关学习资料 http://baike.baidu.com/link?url=77B3BYIuVsB3 ...

  8. JavaWeb学习笔记--Servlet代码集

    目录: 登录系统提交表单数据打开PDFCookieURL传递参数URL重写跟踪会话使用HttpSession对象跟踪会话Servlet间协作过滤器Filter 登录系统 <!DOCTYPE HT ...

  9. Linux系统下搭建DNS服务器——DNS原理总结

    2017-01-07 整理 DNS原理 域名到IP地址的解析过程 IP地址到域名的反向域名解析过程 抓包分析DNS报文和具体解析过程 DNS服务器搭建和配置 这个东东也是今年博主参见校招的时候被很多公 ...

随机推荐

  1. 接口约束的另一种方法:Class类的isAssignableFrom

    Class类的isAssignableFrom是个不常用的方法,感觉这个方法的名字取得不是很好,所以有必要在此解析一下,以免在看源码时产生歧义,这个方法的签名如下: public native boo ...

  2. python3+ selenium3开发环境搭建

    环境搭建 基于python3和selenium3做自动化测试,俗话说:工欲善其事必先利其器:没有金刚钻就不揽那瓷器活,磨刀不误砍柴工,因此你必须会搭建基本的开发环境,掌握python基本的语法和一个I ...

  3. Python 协程 61

    什么是协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程的特点 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到 ...

  4. git 创建项目

    Command line instructions Git global setup git config --global user.name "quliangliang" gi ...

  5. IDEA安装插件提示was not installed: Cannot download解决办法

    打开settings->system settings->updata,把下面的Use secure Connetion去掉

  6. gii配置

    下载完成后不要设置urlManager http://zjm.testyii.com/index.php?r=gii 即可进入gii页面 或者是 将'urlManager'=> [ 'enabl ...

  7. 《Node.js高级编程》之Node 核心API基础

    Node 核心API基础 第三章 加载模块 第四章 应用缓冲区 第五章 事件发射器模式简化事件绑定 第六章 使用定时器制定函数执行计划 第三章 加载模块 本章提要 加载模块 创建模块 使用node_m ...

  8. linux根目录介绍

    1. /bin binary二进制 存放系统许多可执行程序文件 执行的相关指令,例如ls pwd whoami,后台的支持文件目录 2. /sbin super binary超级的二进制 存放系统许多 ...

  9. Linux下Oracle数据库的安装

    记录详细过程以备使用 一.准备安装 为了确保Oracle数据库11g能够成功安装,您需要做好准备工作,例如检查网络配置.更改Linux内核参数.创建用户Oracle.创建安装目录.设置用户Oracle ...

  10. 用apache和tomcat搭建集群,实现负载均衡

    型的企业应用每天都需要承受巨大的访问量,在着巨大访问量的背后有数台服务器支撑着,如果一台服务器崩溃了,那么其他服务器可以使企业应用继续运行,用户对服务器的运作是透明化的,如何实现这种透明化呢?由如下问 ...