不知道Filter是哪位牛人的创意,反正现在的编程框架中到处可见filter的身影,例如struts2、servlet等等。就连tomcat内部的实现,同样也少不了filter
InputFilter OutputFilter都是继承自InputBuffer的接口,两个接口大同小异,下面以InputFilter为例,介绍主要的方法
如何把这些filter装配起来呢?这一节涉及到的内容与 org.apache.coyote.http11.Http11Processor和InternalInputBuffer 有关
首先,在Http11Processor的prepareRequest()方法中,有关于装配filter的代码:
addInputFilter(inputFilters, encodingName);
而在这个方法中,又有如下代码:
if (encodingName.equals("identity")) {
// Skip
} else if (encodingName.equals("chunked")) {
inputBuffer.addActiveFilter
(inputFilters[Constants.CHUNKED_FILTER]);
contentDelimitation = true;
} else {
for (int i = 2; i < inputFilters.length; i++) {
if (inputFilters[i].getEncodingName()
.toString().equals(encodingName)) {
inputBuffer.addActiveFilter(inputFilters[i]);
return true;
}
}
return false;
}
核心代码还是在InternalInputBuffer 类的addActiveFilter方法中,这里才是真正“安装”filter
public void addActiveFilter(InputFilter filter) {
if (lastActiveFilter == -1) {
filter.setBuffer(inputStreamInputBuffer);
} else {
for (int i = 0; i <= lastActiveFilter; i++) {
if (activeFilters[i] == filter)
return;
}
filter.setBuffer(activeFilters[lastActiveFilter]);
}
activeFilters[++lastActiveFilter] = filter;
filter.setRequest(request);
}
在上面的代码中,调用了刚才说到的filter.setBuffer方法,相当于把新加进来的fitler指向activeFilters的最后一个,可见activeFilters相当于一个堆栈,数组第一个元素相当于堆栈的底部
前面已经描述了filter是怎么组装起来的,现在以ChunkedInputFilter为例,主要分析一下InputBuffer.doRead这个很关键的接口方法。
这个filter的作用是解析http字节流中,被chunked了的报文内容(具体的可以参考http规范,据称是http1.1针对长连接的一种传输格式,也可参考我后来发的博客 http://blog.csdn.net/wangchengsi/archive/2009/03/22/4013821.aspx)
filter.setBuffer方法的参数是InputBuffer,故filter要取得http字节流,必定只能通过InputBuffer唯一的一个方法doRead来实现。过程大致如下:
前一个filter通过调用ChunkedInputFilter.doRead方法,可以从中获得所需的数据,而在ChunkedInputFilter.doRead中,ChunkedInputFilter本身又会调用自己前面的那个filter的doRead方法,如此递归下去直到最开头的filter(也就是activeFilters数组的第一个元素)
为了更加清楚地说明整个filter链路的处理过程,假设某个InternalInputBuffer有如下的filter链:
socket JIOEndpoint filter1 <- filter2 <- ..... <- filterN
JIOEndpoint 把socket交给coyote后,经过若干工序,字节流放入 InternalInputBuffer 的 filter1中;然后,调用 InputBuffer的doRead (因为coyote中几乎所有类都是InputBuffer),该doRead调用层层递归,到达 filterN,然后再沿着filter链递归至 filter1,完成数据的“过滤”。最后生成的Request才交给最外围的容器
附带 InternalInputBuffer 的doRead,从这里开始递归调用 filter
public int doRead(ByteChunk chunk, Request req)
throws IOException {
if (lastActiveFilter == -1)
return inputStreamInputBuffer.doRead(chunk, req);
else
return activeFilters[lastActiveFilter].doRead(chunk,req);
}