一般情况下我们通过请求体读取器InputStreamInputBuffer获取的仅仅是源数据,即未经过任何处理发送方发来的字节。但有些时候在这个读取的过程中希望做一些额外的处理,并且这些额外处理可能是根据不同条件做不同的处理,考虑到程序解耦与扩展,于是引入过滤器(过滤器模式)——输入过滤器InputFilter。在读取数据过程中对于额外的操作只需要通过添加不同的过滤器即可实现,例如添加对http1.1协议分块传输的相关操作的过滤器。

如下图,在套接字输入缓冲装置中,从操作系统底层读取的字节会缓冲在buf中,请求行和请求头部被解析后缓冲区buf的指针指向请求体起始位置,通过请求体读取器InputStreamInputBuffer可进行读取操作,它会自动判定buf是否已经读完,读完则重新从操作系统底层读取字节到buf。当其他组件从套接字输入缓冲装置读取请求体时,装置将判定是否包含过滤器,假设有则通过一层层的过滤器完成过滤操作后才能到desBuf,这个过程就像被加入了一道道处理关卡,经过关卡都会被执行相应操作,最终完成源数据到目的数据的操作。

 

过滤器是一种设计模式,在Java的各种框架及容器都有频繁地使用以达到更好的扩展性和逻辑解耦。往下用一个例子看看过滤器如何工作。

① 输入缓冲接口InputBuffer,提供读取操作:

public interface InputBuffer {

   public int doRead(byte[] chunk) throws IOException;

}





② 输入过滤器接口InputFilter,继承InputBuffer类,额外提供setBuffer方法设置前一个缓冲:

public interface InputFilter extends InputBuffer {

public void setBuffer(InputBuffer buffer);

}





③ 输入缓冲装置,模拟通过请求体读取器InputStreamInputBuffer从操作底层获取请求体字节数组,并且里面包含了若干个过滤器,缓冲装置在执行读取操作时会自动判断是否有过滤器,如存在则将读取后的字节在经过层层过滤,得到最终的目的数据。

public class InternalInputBuffer implements InputBuffer {

boolean isEnd = false; 

byte[] buf = new byte[4]; 

protected int lastActiveFilter = -1; 

protected InputFilter[] activeFilters = new InputFilter[2]; 

InputBuffer inputStreamInputBuffer = (InputBuffer) new InputStreamInputBuffer();





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; 

}





public int doRead(byte[] chunk) throws IOException {

if (lastActiveFilter == -1)

return inputStreamInputBuffer.doRead(chunk);

else

return activeFilters[lastActiveFilter].doRead(chunk);

}









protected class InputStreamInputBuffer implements InputBuffer {

public int doRead(byte[] chunk) throws IOException {

if (isEnd == false) {

buf[0] = 'a';

buf[1] = 'b';

buf[2] = 'a';

buf[3] = 'd';

System.arraycopy(buf, 0, chunk, 0, 4);

isEnd = true;

return chunk.length;

} else {

return -1;

}

}

}

}





④ 清理过滤器ClearFilter,负责将读取的字节数组中的字符a换成f:

public class ClearFilter implements InputFilter {

protected InputBuffer buffer;





public int doRead(byte[] chunk) throws IOException {

int i = buffer.doRead(chunk);

if (i == -1)

return -1;

for (int j = 0; j < chunk.length; j++)

if (chunk[j] == 'a')

chunk[j] = 'f';

return i;

}





public InputBuffer getBuffer() {

return buffer;

}





public void setBuffer(InputBuffer buffer) {

this.buffer = buffer;

}

}





⑤ 大写过滤器UpperFilter,负责将读取的字节数组全部变成大写:

public class UpperFilter implements InputFilter {

protected InputBuffer buffer;





public int doRead(byte[] chunk) throws IOException {

int i = buffer.doRead(chunk);

if (i == -1)

return -1;

for (int j = 0; j < chunk.length; j++)

chunk[j] = (byte) (chunk[j] - 'a' + 'A');

return i;

}





public InputBuffer getBuffer() {

return buffer;

}





public void setBuffer(InputBuffer buffer) {

this.buffer = buffer;

}

}





⑥ 测试类,创建输入缓冲装置,接着创建清理过滤器和大写过滤器,把它们添加到输入缓冲装置,执行读取操作,出来的就是经过两个过滤器处理后的数据了,结果为“FBFD”,如果有其他处理需求通过实现InputFilter接口编写过滤器并添加即可。

public class Test {

public static void main(String[] args) {

InternalInputBuffer internalInputBuffer = new InternalInputBuffer();

ClearFilter clearFilter = new ClearFilter();

UpperFilter upperFilter = new UpperFilter();

internalInputBuffer.addActiveFilter(clearFilter);

internalInputBuffer.addActiveFilter(upperFilter);

byte[] chunk = new byte[4];

try {

int i = 0;

while (i != -1) {

i = internalInputBuffer.doRead(chunk);

if (i == -1)

break;

}

} catch (IOException e) {

e.printStackTrace();

}

System.out.println(new String(chunk));

}

}





由于篇幅问题,上面过程已经尽量模拟描述tomcat输入缓冲的工作流程,但实际使用的过滤器并非上面所述,主要包含四个过滤器:IdentityInputFilter、VoidInputFilter、BufferedInputFilter、ChunkedInputFilter。IdentityInputFilter过滤器在http包含content-length头部并且指定的长度大于0时使用,它将根据指定的长度从底层读取响应长度的字节数组,当读取足够数据后将直接返回-1,避免再次执行底层操作;VoidInputFilter过滤器用于拦截读取底层数据操作,当http不包含content-length头部时说明没有请求体,没必要执行读取套接字底层操作,所以用这个过滤器拦截;BufferedInputFilter过滤器负责读取请求体并将其缓存起来,后面读取请求体时直接从此缓冲区读取;ChunkedInputFilter过滤器专门用于处理分块传输,分块传输是一种数据传输机制,当没有指定content-length时可通过分块传输完成通信。

以上就是tomcat的套接字缓冲装置的过滤器的机制及其实现方法,并且简单介绍了tomcat中不同的过滤器的功能,过滤器模式让tomcat在后期程序扩展升级变得更容易。

喜欢研究java的同学可以交个朋友,下面是本人的微信号:

输入过滤器——InputFilter的更多相关文章

  1. Informatica 常用组件Source Qualifier之七 输入过滤器

    通过输入源过滤器,可以降低 PowerCenter  查询的行数.如果在源过滤器中包括字符串 "WHERE" 或较大对象,PowerCenter 将使会话失败. 源限定符转换包括默 ...

  2. 75篇关于Tomcat源码和机制的文章

    75篇关于Tomcat源码和机制的文章 标签: tomcat源码机制 2016-12-30 16:00 10083人阅读 评论(1) 收藏 举报  分类: tomcat内核(82)  版权声明:本文为 ...

  3. Angular JS 学习之过滤器

    1.过滤器可以使用一个管道字符(|)添加到表达式和指令中: 2.AngularJS过滤器可用于转换数据: **currency:格式化数字为货币格式: **filter:从数组项中选择一个子集: ** ...

  4. AngularJS快速入门指南06:过滤器

    thead>tr>th, table.reference>tbody>tr>th, table.reference>tfoot>tr>th, table ...

  5. angualrjs学习总结二(作用域、控制器、过滤器)

    一:Scope简介 Scope(作用域) 是应用在 HTML (视图) 和 JavaScript (控制器)之间的纽带.Scope 是一个对象,有可用的方法和属性.Scope 可应用在视图和控制器上. ...

  6. AngularJS(3)-过滤器

    过滤器可以通过一个管道字符(|)和一个过滤器添加到表达式中.. 1.uppercase/lowercase 大小写过滤器 2.currency过滤器 3.向指令添加过滤器 过滤器可以通过一个管道字符( ...

  7. Wireshark安装、简单使用、过滤器简介

    1.简介 Wireshark是一款非常著名的网络嗅探器,它的前身是Ethereal.Wireshark是一款免费的软件,只需要从官网下根据不同的系统(window,linux等)下载其对应的安装文件即 ...

  8. wireshark基础学习—第四部分wireshark过滤器总结

    这两天一直在熟悉wireshark的过滤器语法规则,以前也接触过这个工具,但只是学校老师教的如何去选择一个接口进行抓取,以及如何去分析一个包的数据.可惜当时对此也没有过多深入.对于我当前,并未接触太多 ...

  9. AngularJS:过滤器

    ylbtech-AngularJS:过滤器 1.返回顶部 1. AngularJS 过滤器 过滤器可以使用一个管道字符(|)添加到表达式和指令中. AngularJS 过滤器 AngularJS 过滤 ...

随机推荐

  1. ubuntu16.04安装eclipse后启动栏图标为问号

    ubuntu创建eclipse快捷方式图标. cd /usr/share/applications sudo touch eclipse.desktop sudo gedit eclipse.desk ...

  2. Android编写点击TextView拨打电话

    在任何一个电商平台都会有点击了手机号码会拨打出一个电话 那么高如何实现这个功能,我们下来分析下原理 当我们点击了一个电话号码后,会弹出一个Dialog显示是否拨打次电话号码,点击确定拨打号码,点击取消 ...

  3. Flexible DEMO 实现手淘H5页面的终端适配

    <!DOCTYPE html> <html> <head> <title>淘宝flexiblejs</title> <meta cha ...

  4. manjaro备忘录

    updated 2018/4/3 manjaro 使用Linux发行版时需要注意几个方面的问题: 包管理器 包管理器无疑时各家发行版的最大特色之一.软件同时也是一个平台是否能够产生足够的吸引力的来源之 ...

  5. C++笔记001:Microsoft Visual Studio 2010软件的安装与建立第一个cpp文件

    原创笔记,转载请注明出处! 点击[关注],关注也是一种美德~ 我学习C++使用软件为Microsoft Visual Studio 2010. 首先,软件的安装包 链接:https://pan.bai ...

  6. 【kmp】 剪花布条 HDU - 2087

    [题意概述] [解题思路] 从左往右找,看有几个匹配的字符串,但要注意例如 aaaaaa  aa的输出是3 而不是5,可以这样理解:画布需要减去,一旦减去也就没有了的 [AC] #include< ...

  7. R语言:安装及使用

    http://blog.csdn.net/pipisorry/article/details/53640638 ubuntu下安装 sudo apt-get install -y r-base源码安装 ...

  8. lucene全文检索基础

    全文检索是一种将文件中所有文本与检索项匹配的文字资料检索方法.比如用户在n个小说文档中检索某个关键词,那么所有包含该关键词的文档都返回给用户.那么应该从哪里入手去实现一个全文检索系统?相信大家都听说过 ...

  9. 亲密接触Redis-第三天(Redis的Load Balance)

    前言 上两天讲述了Redis的基本搭建和基于HA的集群布署方式以及相关的策略和注意点.今天开始讲述Redis的Cluster功能,而这块目前来说网上资料不是太全,就算有1,2篇也只是单讲服务端的搭建也 ...

  10. Mac小技巧:强制退出程序的六种方法

    原帖地址: http://www.cnbeta.com/articles/175447.htm 1.使用键盘快捷键强制退出处于活跃状态的Mac程序 快捷键:Command+Option+Shift+E ...