关于ServletOutputStream源代码引发的探究

在翻阅servlet-src时看到ServletOutputStream的某个实现方式,一知半解的看不明白,代码如下:

public void print(String s) throws IOException {
        if (s == null)
            s = "null";
        int len = s.length();
        for (int i = 0; i < len; i++) {
            char c = s.charAt(i);

            //
            // XXX NOTE: This is clearly incorrect for many strings,
            // but is the only consistent approach within the current
            // servlet framework. It must suffice until servlet output
            // streams properly encode their output.
            //
            if ((c & 0xff00) != 0) { // high order byte must be zero
                String errMsg = lStrings.getString("err.not_iso8859_1");
                Object[] errArgs = new Object[1];
                errArgs[0] = Character.valueOf(c);
                errMsg = MessageFormat.format(errMsg, errArgs);
                throw new CharConversionException(errMsg);
            }
            write(c);
        }
    }

其中”if ((c & 0xff00) != 0) { // high order byte must be zero”这句话是什么意思?我们知道在java中,一个字符占两个字节,共16位,0xff00不就是11111111 11111111 00000000 00000000.假如字符串是纯英文,经测试能正常显示。但假如是汉字呢?
测试代码:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class OutputStreamServlet extends HttpServlet
{
    public void service(ServletRequest req,
           ServletResponse res)
             throws ServletException,
                    java.io.IOException{
        res.setCharacterEncoding("utf-8");  //该语句并不能解决编码问题  
        ServletOutputStream outputStream = res.getOutputStream();  
        outputStream.println("简体中文"); 
    }
}

用前天配置虚拟路径的方法测试了下:
http://blog.csdn.net/nanphonfy/article/details/48749877
javac编译好的.class文件拷到D:\myWeb\WEB-INF\classes目录下,在浏览器中访问:
http://localhost:8080/it315/test.html
这里写图片描述
如上出现了编码问题。

而源代码中public abstract class ServletOutputStream extends OutputStream{…}可知javax.servlet.ServletOutputStream是个抽象类。
在以前早期的版本,不会出现这个编码问题,因为我们运行用的类的方法不是它本身,而是它的子类,所以这个子类一定是最终被tomcat实现了。
即这个println(String s)函数以前是被子类重写,覆盖了。
经过分析tomcat代码最终找到了真正的实现类,路径如下apache-tomcat-7.0.28-src\java\org\apache\catalina\connector\CoyoteOutputStream.java,在里面并没有重写println(String s)
当然tomcat7不再覆盖println(String s)方法也是有道理的,就是让我们使用Writer而不再使用OutputStream来输出HTML或XML之类的内容。

用PrintWriter来测试:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class OutputStreamServlet extends HttpServlet
{
    public void service(ServletRequest req,
           ServletResponse res)
             throws ServletException,
                    java.io.IOException{
        res.setCharacterEncoding("utf-8"); 
        PrintWriter writer = res.getWriter();  
        writer.println("简体中文");  
    }
}

正常显示。

分析源代码
apache-tomcat-7.0.28-src\java\org\apache\catalina\connector\CoyoteWriter.java

 @Override
    public void print(String s) {
        if (s == null) {
            s = "null";
        }
        write(s);
    }
 @Override
    public void write(String s) {
        write(s, 0, s.length());
    }
 @Override
    public void write(String s, int off, int len) {
        if (error) {
            return;
        }
        try {
            ob.write(s, off, len);//这个ob在前面有定义:protected OutputBuffer ob;
        } catch (IOException e) {
            error = true;
        }
    }

继续查看OutputBuffer的代码,发现如下方法:

 /**
     * Append a string to the buffer
     */
    @Override
    public void write(String s, int off, int len)
        throws IOException {

        if (suspended) {
            return;
        }
        charsWritten += len;
        if (s == null) {
            s = "null";
        }
        conv.convert(s, off, len);
        //这里进行编码转换,conv的声明:protected C2BConverter conv;  
        //在调试过程中可以看到C2BConverter中的存放的正是utf-8编码。 
    }

总结:
OutputStream输出的是二进制流(字节流),涉及到汉字的要用char来存储处理,此时要考虑用Writer。
参考:
http://blog.csdn.net/hengyunabc/article/details/17056237
http://gongsy.iteye.com/blog/516740

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值