软件设计的一个核心问题是能否使用重复的体系架构,即能否达到体系架构级的软件重用。也就是说,能否在不同的软件系统中,使用同一体系架构。基于这个目的,许多学者们开始研究和实践软件体系架构的模式问题。在<Pattern-Oriented Software Architecture (面向模式的软件体系架构) >中首次提出了8种体系结构模式:
层(L a y e r s)、管道和过滤器(Pipes and Filters) 、黑板( B l a c k b o a r d )、代理者( B r o k e r)、模型-视图-控制器( M o d e l - Vi e w - C o n t r o l l e r)、表示-抽象-控制(P r e s e n t a t i o n - A b s t r a c t i o n - C o n t r o l)、微核(M i c r o k e r n e l)、映像(R e f l e c t i o n)。
管道和过滤器(Pipes and Filters)体系架构模式是为处理数据流的系统提供的一种模式。它是由过滤器和管道组成的.每个处理步骤都被封装在一个过滤器组件中,数据通过相邻过滤器之间的管道进行传输。每个过滤器可以单独修改,功能单一,并且它们之间的顺序可以进行配置。下图是管道/过滤器模式的示意图。一个典型的管道/过滤器体系结构的例子是以Unix shell编写的程序。Unix既提供一种符号,以连接各组成部分(Unix的进程),又提供某种进程运行时机制以实现管道。另一个著名的例子是传统的编译器。传统的编译器一直被认为是一种管道系统,在该系统中,一个阶段(包括词法分析、语法分析、语义分析和代码生成)的输出是另一个阶段的输入。

4.1 Servlet Filter概述
凡是开发过J2EE的web application的人员都知道,经常需要处理以下几种情况:
- 访问特定资源(Web 页、JSP 页、servlet)时的身份认证
- 应用程序级的访问资源的审核和记录
- 应用程序范围内对资源的加密访问,它建立在定制的加密方案基础上
- 对被访问资源的及时转换, 包括从 servlet 和 JSP 的动态输出
在servlet2.3之前这些功能处理是很难实现的,但是Java Servlet 2.3 规范新增了不少激动人心的功能,其中之一便是过滤器(Filter),其实这就是我们所说的管道和过滤器体系架构在J2EE中的应用实践. 通过使用该模式使得Web Application开发者能够在请求到达Web资源之前截取请求,在处理请求之后修改应答。其结构图如下:

一个执行过滤器的Java 类必须实现javax.servlet.Filter 接口。这一接口含有三个方法:
- init(FilterConfig):这是容器所调用的初始化方法。它保证了在第一次 doFilter() 调用前由容器调用。它能获取在 web.xml 文件中指定的filter初始化参数。
- doFilter(ServletRequest, ServletResponse, FilterChain):这是一个完成过滤行为的方法。它同样是上一个过滤器调用的方法。引入的 FilterChain 对象提供了后续过滤器所要调用的信息。
- destroy():容器在销毁过滤器实例前,doFilter()中的所有活动都被该实例终止后,调用该方法。

4.2 Filter链介绍
所有过滤器都服从调用的过滤器链,并通过定义明确的接口得到执行。WebApplication可以指定许多过滤器来完成相关的工作.那么它们就组成一个过滤器链来完成相应的工作.其结构如下图:

4.3 例子
4.3.1 简单filter
在PetStore1.3.1中的就存在两个Filter过滤器.其中一个过滤器,完成字符集的编码的转化,如大家经常遇到的汉字编码问题,你只需配置为GBK即可.它从Web.xml之中读取这些参数的配置信息,然后进行编码的转化.另一个是安全校验Fliter,它负责进行安全检查哪些页面可以进行,哪些不可.它们组成一个Filter链,结构图和实现代码如下(完整代码参见附件):

|
容器通过 Web 应用程序中的配置描述符 web.xml 文件解析过滤器配置信息。有两个新的标记与过滤器相关:<filter> 和 <filter-mapping>。<filter> 标记是一个过滤器定义,它必定有一个 <filter- name> 和 <filter-class> 子元素。<filter-name> 子元素给出了一个与过滤器实例相关的名字。<filter-class> 指定了由容器载入的实现类。您能随意地包含一个 <init-param> 子元素为过滤器实例提供初始化参数。<filter-mapping> 标记代表了一个过滤器的映射,指定了过滤器会对其产生作用的 URL 的子集。
|
4.3.2 复杂的filter
上面是petstore的例子,演示了通过Fliter修改字符编码和安全认证的功能.下面提供一个示例演示通过修改返回数据(通过过滤器把response的字符串变成大写).
|
该示例使用HttpServletResponseWrapper技术,它是对HttpServletResponse的包装,其实就是装饰(decorate)设计模式的应用.这个例子能够工作的关键是UCaseResponse和UCaseWriter类,它实现了对每个要输出的字符都转成了大写后再写入实际的输出流的功能。
4.4 体系架构的实现
实现一个管道和过滤器一般要注意以下几个方面:
- 把系统任务分成一系列处理阶段。
根据管道和过滤器的设计方案,必须把系统处理的任务分割成相应独立的任务,如日志,数据转化,安全认证等.这样每个阶段仅依赖其前一阶段的输出。通过数据流将所有阶段相连起来。并且你可以进行替换每个步骤,或者可以调整它们之间的顺序,以产生新的结果.
如petstore中的编码转化Filter和安全Filter,分成两个独立的处理阶段. - 定义沿每个管道传输的数据格式。
我们知道每个过滤器,定义一个统一格式以获得最大的灵活性,因为它使过滤器的重组变得容易。如:每个过滤器的方法是doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)它们的参数是必须相同的. - 决定如何实现每个管道连接
Filter过滤器的连接是推得方式来实现的.前一个过滤器主动的调用filterChain.doFilter(request, response);来实现转向下一个过滤器. - 设计和实现过滤器
设计每个Filter具有独立的功能,如编码转化,安全校验,等功能.并且每个Fliter都应该在实现javax.servlet.Filter接口. - 建立处理流水线
过滤器的部署是在Web.xml中进行配置,描述过滤器的实现类以及它们的map关系,来确定它们的顺序.
在使用管道和过滤器模式时,一般会使用以下的GOF设计模式.
6.1 职责链模式(Chain Of Responsibility)
职责链设计模式的意图就是使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。其实管道和过滤器模式就是职责链模式的抽象,把它应用到软件体系架构中.
6.2 命令模式(Command)
命令模式的意图将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。在管道和过滤器模式中每个过滤器一般使用命令模式,把请求封装成一个命令进行处理.
6.3 装饰模式(Decorator)
装饰模式的意图就是动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
在管道和过滤器模式中,在每个过滤器中经常需要对请求进行动态的增加功能,或者修改请求的内容,这时一般会使用装饰模式.如Servlet filter的javax.servlet.http.HttpServletRequestWrapper, javax.servlet.http.HttpServletResponseWrapper就是装饰模式的应用.