上篇我们介绍了 tomcat 是怎么对接收到字符进行编码的,现在我们来看当向客户端写 html 文档的时候到底发生了什么?
tomcate 在向 客户端写出数据 的時候 ,使用的是 response 的 输出 流來 实现的 。 但是 jsp 是怎樣使用 response 的 流的 呢?
在使用 JSP 内含對象 out 輸出的時候, out 是一個 JspWriter 实现类的对象实例, JspWriterImpl(ServletResponse response, int sz, boolean autoFlush) 是一 个该类的构造函数 , 其使用到了 response ,在 JspWriterImpl 内部 还有一个 java.io.Writer 对象实例的引用 ,在使用 JspWriter (JSP 的 out 对象 ) 写出数据的时候,会调用如下的函数来初始化
protected void initOut() throws IOException
{
if(out == null)
{
out = response.getWriter();/ 初始化 java.io.Writer 對象
}
} 来初始化该内部对象 的。
然后 在 jspWriter 的 各个输出数据的函数的实现中 就是調用上面的 java.io.Writer 對象的方法的。
所以 不论 是 jsp 或者是 servlet, 对客户端写出 html 的時候,都是 通过 response.getWriter(); 来 得到的字符流或者由 getOutputStream() 得到 2 进制 流的。
一個 response 存在一個字符流,也存在一個 2 進制流,但是在同一時刻只能打開使用一個流的。至於兩者的關係,我們在後面介紹。 Jsp 的 out 對象就是 response 的字符流的。
同樣的 request 也存在一個字符流和一個 2 進制流,但是在同一時刻只能打開使用一個流的。
response 的 两个流的关系
我们来考察 response 的 实现类 的 getOutputStream() 和 getWriter 函数的实现 :
public ServletOutputStream getOutputStream() throws IOException
{
。。。。。。。。。。。。。。。。。。。。。
stream = createOutputStream();/// 创建 response 的 2 进制 的 输出流
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
return stream;
}
public PrintWriter getWriter() throws IOException
{
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
ResponseStream newStream = (ResponseStream)createOutputStream(); 创建 2 进制 流
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
OutputStreamWriter osr = new OutputStreamWriter(newStream, getCharacterEncoding());
writer = new ResponseWriter(osr, newStream);/// 得到 response 的字符 输出 流
。。。。。。。。。。。。。。。。。。。。。。。。。。
}
}
显然 , 我们的字符流就是从 2 进制 流 转化而来的
还有两个函数要注意:
public String getCharacterEncoding()//response 的 编码 , 默认是 ISO-8859-1 的
{
if(encoding == null) // 如果没有指定编码
{
return "ISO-8859-1";
} else
{
return encoding;
}
}
public void setContentType(String type) ; 设置 response 的 类型和编码
{
。。。。。。。。。。。。。
encoding = RequestUtil.parseCharacterEncoding(type); 得到 指定的编码
if(encoding == null)
{
encoding = "ISO-8859-1";// 如果沒有指定 编码 方式
}
} else
if(encoding != null)
{
contentType = type + ";charset=" + encoding;
}
}
好了, 现在我们知道了在写出字符的时候使用的 response 的字符流 ( 不管是 jsp 或者 servlet), 也就是使用的 OutputStreamWriter osr = new OutputStreamWriter(newStream, getCharacterEncoding());
注意的是 newStream 是 response 的 2 进制流的实现 。
所以 我们还得看看 OutputStreamWriter 的 实现 :
考察 OutputStreamWriter 的源代碼,他 有 一個 StreamEncoder 类型的对象 ,就是依靠他來 转换编码的 ;
StreamEncoder 是由 sun 公司提供的,它 有一个
public static StreamEncoder forOutputStreamWriter(OutputStream outputstream, Object obj, String s) 來得到 StreamEncoder 对象实例 。
对于 jsp,servlet 来说在构造他的时候 outputstream 参数 是 response 的 2 进制流 , obj 是 OutputStreamWriter 对象 , s 就是 编码方式的名字 。 其实 得到是一個 StreamEncoder 的 子类的对象实例 ,
return new CharsetSE(outputstream, obj, Charset.forName(s1)); CharsetSE 是 StreamEncoder 的 子类。
他有 一个如下的函数来实现编码转换的 :
void implWrite(char ac[], int i, int j)throws IOException /// ac 是要輸出 String 的 char 數組
{
CharBuffer charbuffer = CharBuffer.wrap(ac, i, j);
。。。。。。。。。。。。。。。。。。。。。。。
CoderResult coderresult = encoder.encode(charbuffer, bb, false);/bb 是 ByteBuffer ,存放 编码后的 byte 缓冲区
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
writeBytes();/// 將 bb 转化 到 byte 数组写入 到 response 的 2 进制流中
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
}
至此,我们了解了 tomcat 背后的编码转换过程