一、Servlet Filter概述java
凡是开发过J2EE的web application的人员都知道,常常须要处理如下几种状况:web
访问特定资源(Web 页、JSP 页、servlet)时的身份认证apache
应用程序级的访问资源的审核和记录设计模式
应用程序范围内对资源的加密访问,它创建在定制的加密方案基础上安全
对被访问资源的及时转换, 包括从 servlet 和 JSP 的动态输出服务器
在servlet2.3以前这些功能处理是很难实现的,可是Java Servlet 2.3 规范新增了很多激动人心的功能,其中之一即是过滤器(Filter),其实这就是咱们所说的管道和过滤器体系架构在J2EE中的应用实践. 经过使用该模式使得Web Application开发者可以在请求到达Web资源以前截取请求,在处理请求以后修改应答。其结构图以下:架构
500){this.resized=true;this.style.width=500;}" align=center vspace=1 border=1>并发 |
一个执行过滤器的Java 类必须实现javax.servlet.Filter 接口。这一接口含有三个方法:app
init(FilterConfig):这是容器所调用的初始化方法。它保证了在第一次 doFilter() 调用前由容器调用。它能获取在 web.xml 文件中指定的filter初始化参数。模块化
doFilter(ServletRequest, ServletResponse, FilterChain):这是一个完成过滤行为的方法。它一样是上一个过滤器调用的方法。引入的 FilterChain 对象提供了后续过滤器所要调用的信息。
destroy():容器在销毁过滤器实例前,doFilter()中的全部活动都被该实例终止后,调用该方法。
500){this.resized=true;this.style.width=500;}" align=center vspace=1 border=1> |
二、Filter链介绍
全部过滤器都服从调用的过滤器链,并经过定义明确的接口获得执行。WebApplication能够指定许多过滤器来完成相关的工做.那么它们就组成一个过滤器链来完成相应的工做.其结构以下图:
500){this.resized=true;this.style.width=500;}" align=center vspace=1 border=1> |
三、例子
3.1 简单filter
在PetStore1.3.1中的就存在两个Filter过滤器.其中一个过滤器,完成字符集的编码的转化,如你们常常遇到的汉字编码问题,你只需配置为GBK便可.它从Web.xml之中读取这些参数的配置信息,而后进行编码的转化.另外一个是安全校验Fliter,它负责进行安全检查哪些页面能够进行,哪些不可。它们组成一个Filter链,结构图和实现代码以下:
500){this.resized=true;this.style.width=500;}" align=center vspace=1 border=1> |
public class EncodingFilter implements Filter { private FilterConfig config = null; // default to ASCII private String targetEncoding = "ASCII"; public void init(FilterConfig config) throws ServletException { this.targetEncoding = config.getInitParameter("encoding"); } //在过滤器中实现字符集编码转化 public void doFilter(ServletRequest srequest, ServletResponse sresponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)srequest; request.setCharacterEncoding(targetEncoding); // move on to the next chain.doFilter(srequest,sresponse); } public void destroy() { ................. } } public class SignOnFilter implements Filter { public void init(FilterConfig config) throws ServletException { this.config = config; URL protectedResourcesURL = null; try { protectedResourcesURL = config.getServletContext().getResource("/WEB-INF/signon-config.xml"); ............... } catch (java.net.MalformedURLException ex) { System.out.println("SignonFilter: malformed URL exception: " + ex); } } public void destroy() { config = null; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ........ } } |
容器经过 Web 应用程序中的配置描述符 web.xml 文件解析过滤器配置信息。有两个新的标记与过滤器相关:<filter> 和 <filter-mapping>。<filter> 标记是一个过滤器定义,它一定有一个 <filter- name> 和 <filter-class> 子元素。<filter-name> 子元素给出了一个与过滤器实例相关的名字。<filter-class> 指定了由容器载入的实现类。您能随意地包含一个 <init-param> 子元素为过滤器实例提供初始化参数。<filter-mapping> 标记表明了一个过滤器的映射,指定了过滤器会对其产生做用的 URL 的子集。
<!-- Encoding Filter Declaration Start --> <filter> <filter-name>EncodingFilter</filter-name> <display-name>Encoding Filter</display-name> <description>no description</description> <filter-class>com.sun.j2ee.blueprints.encodingfilter.web.EncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <!-- Encoding Filter Declaration End --> <!-- Signon Filter Declaration Start --> <filter> <filter-name>SignOnFilter</filter-name> <display-name>SignOn Filter</display-name> <description>no description</description> <filter-class>com.sun.j2ee.blueprints.signon.web.SignOnFilter</filter-class> </filter> <!-- Signon Filter Declaration End --> <!-- Encoding Filter Mapping Start--> <filter-mapping> <filter-name>EncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- Encoding Filter Mapping End --> <!-- Signon Filter Mapping Start--> <filter-mapping> <filter-name>SignOnFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- Signon Filter Mapping End --> |
3.2 复杂的filter
上面是petstore的例子,演示了经过Fliter修改字符编码和安全认证的功能。下面提供一个示例演示经过修改返回数据(经过过滤器把response的字符串变成大写)。
public class UCaseResponse extends HttpServletResponseWrapper { public UCaseResponse(HttpServletResponse response) { super(response); } public PrintWriter getWriter() throws IOException { return new UCaseWriter(super.getWriter()); } } public class UCaseWriter extends PrintWriter { public UCaseWriter(Writer out) { super(out); } public void write(int c) { super.write(Character.toUpperCase( (char) c)); } public void write(char buf[], int off, int len) { for (int i = 0;i < len;i++) { write(buf[off + i]); } } public void write(String s, int off, int len) { for (int i = 0;i < len;i++) { write(s.charAt(off + i)); } } } public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) { try { filterChain.doFilter(request, new UCaseResponse((HttpServletResponse)(response))); }catch(Exception sx) { filterConfig.getServletContext().log(sx.getMessage()); } |
该示例使用HttpServletResponseWrapper技术,它是对HttpServletResponse的包装,其实就是装饰(decorate)设计模式的应用.这个例子可以工做的关键是UCaseResponse和UCaseWriter类,它实现了对每一个要输出的字符都转成了大写后再写入实际的输出流的功能。
四、体系架构的实现
实现一个管道和过滤器通常要注意如下几个方面:
把系统任务分红一系列处理阶段。
根据管道和过滤器的设计方案,必须把系统处理的任务分割成相应独立的任务,如日志,数据转化,安全认证等。这样每一个阶段仅依赖其前一阶段的输出。经过数据流将全部阶段相连起来。而且你能够进行替换每一个步骤,或者能够调整它们之间的顺序,以产生新的结果.如petstore中的编码转化Filter和安全Filter,分红两个独立的处理阶段。
定义沿每一个管道传输的数据格式。
咱们知道每一个过滤器,定义一个统一格式以得到最大的灵活性,由于它使过滤器的重组变得容易。如:每一个过滤器的方法是doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)它们的参数是必须相同的。
决定如何实现每一个管道链接
Filter过滤器的链接是推得方式来实现的.前一个过滤器主动的调用filterChain.doFilter(request, response);来实现转向下一个过滤器。
设计和实现过滤器
设计每一个Filter具备独立的功能,如编码转化,安全校验,等功能.而且每一个Fliter都应该在实现javax.servlet.Filter接口。
创建处理流水线
过滤器的部署是在Web.xml中进行配置,描述过滤器的实现类以及它们的map关系,来肯定它们的顺序。
其余应用实例
一、JBOSS
若是你们对EJB了解,应该知道客户调用EJB实际上并非直接引用EJB实例(ejb instance).它经过容器来截获客户端的请求,而后按照EJB描述符作些许多相应的工做如,安全校验,事务的处理,线程并发处理等.这样就可使开发人员仅关心本身的业务逻辑,而不需对复杂的基础服务进行实现.使开发人员从繁琐的工做中解脱出来.集中精力处理本身的业务逻辑,它的结构图以下:
500){this.resized=true;this.style.width=500;}" align=center vspace=1 border=1> |
笔者有幸阅读了JBOSS的源码,分析了Jboss的EJB容器的实现. EJB的容器经过许多拦截器(Inteceptor)来实现,每一个拦截器处理必定的功能,一个处理结束后转发给下一个拦截器,最后一个拦截器才把真正调用EJB的实例.其中它的EntityBean 容器的拦截器的结构以下:
500){this.resized=true;this.style.width=500;}" align=center vspace=1 border=1> |
咱们看其中的log拦截器
public class LogInterceptor extends AbstractInterceptor{ public Object invoke(Invocation invocation) throws Exception { boolean trace = log.isTraceEnabled(); // Log call details if (callLogging) { ......进行log的处理 } //处理结束,把请求转发给下一个拦截器 return getNext().invoke(invocation); } |
这些拦截器配置在standardjboss.xml文件中,以下:
<container-interceptors> <interceptor>org.jboss.ejb.plugins.LogInterceptor</interceptor> <interceptor>org.jboss.ejb.plugins.SecurityInterceptor</interceptor> <interceptor>org.jboss.ejb.plugins.TxInterceptorCMT</interceptor> <interceptor metricsEnabled="true">org.jboss.ejb.plugins.MetricsInterceptor</interceptor> <interceptor>org.jboss.ejb.plugins.EntityLockInterceptor</interceptor> <interceptor>org.jboss.ejb.plugins.EntityInstanceInterceptor</interceptor> <interceptor>org.jboss.resource.connectionmanager.CachedConnectionInterceptor</interceptor> <interceptor>org.jboss.ejb.plugins.EntitySynchronizationInterceptor</interceptor> <interceptor>org.jboss.ejb.plugins.cmp.jdbc.JDBCRelationInterceptor</interceptor> </container-interceptors> |
这就是Jboss容器架构最巧妙的地方,最初这个架构就是由天才少年Rickard Oberg提出的.其实这个架构就应用咱们讨论的管道和过滤器模式,其实每一个拦截器就是一个过滤器,使用该模式给Jboss带来了以下的好处:
使系统的架构更容易理解,由于每一个过滤器完成单一的功能。
使系统更加模块化,有利于系统的模块重用和扩展,若是系统想增长某种功能只需增长一个实现Interceptor接口的拦截器,而后配置在standardjboss.xml文件中便可。
使系统的容易进行错误处理,若是在一个拦截器中发现错误(error)或者异常(exception),只需返回便可.
二、AXIS
无独有偶,一样在Axis上也应用了管道和过滤器模式.Aixs是apache开源的webservice实现服务器。简单的说,axis就是处理Message,它首先截获客户端的请求,而后转发到真正的实现业务逻辑上处理客户端的请求,在这以前通过一系列的handler处理.它的结构很像EJB容器.其实就是管道和过滤器模式的应用,Handler就是过滤器.它的处理顺序主要考虑两个方面一个是部署描述符(deployment configuration )另外一个就是是客户端仍是服务器端。Handler处理的对象是MessageContext它的由3个重要的部分组成,一是一个request Message,一个是response message,还有许多属性。
咱们经研究源码分析,在服务器端,有一个Transport Listener 它监听客户端的请求, 能够经过多种协议,一旦有客户请求,它将按照协议的规范把数据解析生成生成一个Message对象,而后把它设置到MessageContext,而后调用一系列的Handler进行处理。
其结构图以下:
500){this.resized=true;this.style.width=500;}" align=center vspace=1 border=1> |
相关设计模式
在使用管道和过滤器模式时,通常会使用如下的GOF设计模式。
一、职责链模式(Chain Of Responsibility)
职责链设计模式的意图就是使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。其实管道和过滤器模式就是职责链模式的抽象,把它应用到软件体系架构中。
二、 命令模式(Command)
命令模式的意图将一个请求封装为一个对象,从而使你可用不一样的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操做。在管道和过滤器模式中每一个过滤器通常使用命令模式,把请求封装成一个命令进行处理。
三、装饰模式(Decorator)
装饰模式的意图就是动态地给一个对象添加一些额外的职责。就增长功能来讲,Decorator模式相比生成子类更为灵活。
在管道和过滤器模式中,在每一个过滤器中常常须要对请求进行动态的增长功能,或者修改请求的内容,这时通常会使用装饰模式.如Servlet filter的javax.servlet.http.HttpServletRequestWrapper, javax.servlet.http.HttpServletResponseWrapper就是装饰模式的应用.
总结
本文讨论了管道和过滤器模式的解决方案,以及它的优缺点.而后以J2EE规范的Servlet Filter为例详细介绍了怎样应用该模式,同时简单的介绍了在Jboss,Axis中的应用。