在tomcat核心处理中有这么一个需求——“为了提高编码性能,对于socket接收到的字节流不马上进行某种编码的转码,而是应该保留字节流的形式,在需要时、在指定编码时才进行转码工作”。MessageBytes正是为解决这个问题而提出的一个类。
消息字节封装了不同类型方式用于表示信息,它包含了四种类型:T_BYTES、T_CHARS、T_STR、T_NULL,分别表示字节类型、字符类型、字符串类型、空。由于web服务器通信过程使用ASCII码通信,对应的是字节,所以这里选取T_BYTES类型作为案例说明,其他类型与之类似。消息字节的使用方法很简单,假如有一个字节数组byte[] buffer,它坐标的第3到第20之间的字节数组组成的字符表示Request对象中的方法变量的值,那么用以下代码简单表示:
① public class Request{
MessageBytes methodMB = new MessageBytes();
public MessageBytes method() {
returnmethodMB;
}
}
② Request request = new Request();
request.method().setBytes(buffer, 3, 18);
执行上面操作后就完成对字节数组的某段进行标记操作,方便以后获取指定的一段字节数组,参照下图,你可以用多个消息字节对buffer标记,例如对请求变量、协议版本等变量进行标记,每个消息字节实例标识了一段字节数组,可以通过如下获取并转为字符串类型:
request.method().toString();
使用起来很简单,接着看下实际实现原理,化繁为简,由于tomcat底层接收的是字节流,于是只考虑T_BYTES的情况。
① MessageBytes类
public class MessageBytes {
private finalByteChunk byteC = new ByteChunk();
public voidsetBytes(byte[] b, int off, int len) {
byteC.setBytes(b,off, len);
}
public StringtoString() {
returnbyteC.toString();
}
}
② ByteChunk类
public class ByteChunk {
privatebyte[] buff;
private intstart = 0;
private intend;
public voidsetBytes(byte[] b, int off, int len) {
buff= b;
start= off;
end= start + len;
}
public StringtoString() {
Charsetcharset=Charset.forName("ISO_8859_1");
CharBuffercb = charset.decode(ByteBuffer.wrap(buff, start, end - start));
returnnew String(cb.array(), cb.arrayOffset(), cb.length());
}
}
前面示例中methodMB.setBytes(buffer, 3, 18)其实是调用了ByteChunk的setBytes方法,把字节流及始末坐标设置好,后面methodMB.toString()同样调用了ByteChunk的toString方法,根据指定编码进行转码,这里是ISO_8859_1。这样一来就达到了延迟处理模式的效果,在需要时才根据指定编码转码并获取字符串,不需要的话则无需转,处理性能得到提高。
Tomcat对于socket接收的信息都用消息字节表示,好处是实现一种延迟处理模式,提高性能。而且实际上tomcat还引入了字符串缓存,在转码之前会先从缓存中查找是否有对应的编码的字符串,如果存在则不必再执行转码动作而直接返回对应的字符串,性能进一步得到优化。为了性能我们必须要多做一些额外的工作,这也是tomcat接收到的信息为何不直接用字符串String保存的原因。