Servlet过滤器是可插入的Web组件,使我们能够在Web应用程序中实现预处理和后处理逻辑。 过滤器支持Servlet和JSP页面的基本请求处理功能,例如日志记录,性能,安全性,会话处理,XSLT转换等。 过滤器是与Java Servlet 2.3规范一起首次发布的,它与最近完成的2.4规范进行了认真的升级。 在J2EE探路者系列的最后一部分中,我将向您介绍Servlet过滤器的基础知识,例如总体体系结构设计,实现细节以及J2EE Web应用程序中的典型用法,并且还将介绍一些内容。您可以从最新的Servlet规范中获得预期的扩展功能。
什么是Servlet过滤器?
Servlet筛选器是小型Web组件,可拦截请求和响应以查看,提取或以某种方式处理客户端和服务器之间交换的数据。 筛选器是Web组件,通常封装一些功能,这些功能虽然很重要,但对于处理客户端请求或发送响应而言并不重要。 典型示例包括记录有关请求或响应的数据,处理安全协议,管理会话属性等等。 筛选器提供了一种模块化的,面向对象的机制,用于将常见任务封装到可插拔组件中,这些可插拔组件通过配置文件进行声明并进行动态处理。
Servlet过滤器中结合了许多元素,使它们成为独特,强大且模块化的Web组件。 即,Servlet过滤器为:
- 声明性的 :过滤器是通过Web部署描述符(web.xml)中的XML标记声明的。 这样就可以在不触及任何应用程序代码或JSP页面的情况下添加和删除过滤器。
- 动态的 :在运行时,Servlet容器调用过滤器以拦截和处理请求和响应。
- 灵活 :过滤器在Web处理环境中的应用广泛,涵盖了许多最常见的辅助任务,例如日志记录和安全性。 筛选器也很灵活,因为它们可用于直接对来自客户端的呼叫执行预处理和后处理,以及处理防火墙后Web组件之间的分派请求。 最后,过滤器可以链接在一起以提供所需的功能。
- 模块化 :通过将应用程序处理逻辑封装到单个类文件中,过滤器定义了模块化单元,可以轻松地将其添加到请求/响应链中或从中删除。
- 可移植 :与Java平台的许多方面一样,Servlet过滤器可跨平台和容器移植,从而进一步增强了Servlet过滤器的模块化和可重用质量。
- 可重用 :由于过滤器实现类的模块化设计以及配置过滤器的声明方式,因此可以在项目和应用程序之间轻松使用过滤器。
- 透明的 :在请求/响应链中包含过滤器的目的是为了补充但绝不能替代servlet或JSP页面提供的核心处理。 因此,可以根据需要添加或删除过滤器,而不会破坏servlet或JSP页面。
因此,Servlet过滤器是模块化的,可重用的组件,可以通过配置文件灵活地声明。 过滤器动态处理传入的请求和传出的响应,并且可以透明地添加和删除,而无需修改应用程序代码。 最后,过滤器独立于任何平台或Servlet容器,从而使它们可以轻松部署在任何兼容的J2EE环境中。
在以下各节中,我们将仔细研究Servlet过滤器机制的总体设计以及实现,配置和部署过滤器所涉及的步骤。 我们还将探讨Servlet过滤器的一些实际用途,并简要介绍一下Model-View-Controller体系结构中包含Servlet过滤器的情况。
Servlet过滤器架构
顾名思义, Servlet过滤器用于拦截传入的请求和/或传出的响应,并监视,修改或以某种方式处理正在通过的数据流。 筛选器是独立的模块化组件,可以添加到请求/响应链中,也可以将其删除而不会影响应用程序中的其他Web组件。 过滤器仅改变请求和响应的运行时处理,因此,除非通过Servlet API中定义良好的标准接口,否则不应将其直接绑定到Web应用程序框架中。
可以将Web资源配置为不具有与之关联的过滤器(默认),单个过滤器(典型)或什至一连串的过滤器。 那么过滤器有什么作用? 像servlet一样,它接收一个请求和响应对象。 然后,筛选器将检查请求对象,并决定将请求转发到链中的下一个组件,或者停止请求并将响应直接发送回客户端。 如果请求被转发,它将被传递到链中的下一个资源(另一个过滤器,一个servlet或JSP页面)。 请求通过链处理并由服务器处理后,响应以相反的顺序通过链发送回去。 这使每个过滤器都有机会在必要时处理响应对象。
最初在Servlet 2.3规范中引入过滤器时,它们只能过滤Web客户端和客户端正在访问的指定Web资源之间的内容。 如果该资源随后将请求分发到其他Web资源,则无法将筛选器应用于在后台委派的任何请求。 对于2.4规范,此限制已被删除。 现在,可以在J2EE Web环境中存在请求和响应对象的任何地方应用Servlet过滤器。 因此,可以在客户端和Servlet之间,Servlet与Servlet或JSP页面之间以及每个包含的JSP页面之间应用Servlet筛选器。 这就是我所说的力量和灵活性!
实现Servlet过滤器
他们说,所有的好事都是三分法。 我不知道谁是“他们”,或者不知道这句老话有多少道理,但是实现Servlet过滤器需要三个步骤。 首先,对过滤器实现类进行编程,然后将过滤器添加到Web应用程序中(通过在Web部署描述符/web.xml中声明),最后,将应用程序与过滤器打包并进行部署。 我们将详细介绍每个步骤。
1.对实现类进行编程
过滤器API由三个简单的接口(又是三个接口)组成,它们紧密地嵌套在javax.servlet
包中。 这三个接口是Filter
, FilterChain
和FilterConfig
。 从编程的角度来看,您的过滤器类将实现Filter
接口,然后在过滤器类中使用FilterChain
和FilterConfig
接口。 您的过滤器类将传递对FilterChain
对象的引用,以允许过滤器将控制权传递给链中的下一个资源。 容器将把FilterConfig
对象提供给过滤器,以提供对过滤器初始化数据的访问。
与我们的三元模式保持一致,过滤器必须应用三种方法才能完全实现Filter
接口:
-
init()
:当容器实例化过滤器时,将调用此方法,该方法旨在为过滤器做准备以进行处理。 该方法接受FilterConfig
类型的对象作为输入。 -
doFilter()
:与Servlet具有service()
方法(依次调用doPost()
或doGet()
)来处理请求的方式相同,过滤器具有用于处理请求和响应的单个方法doFilter()
。 此方法接受三个输入参数:ServletRequest
,response
和FilterChain
对象。 -
destroy()
:如您所料,此方法对类进行自动垃圾收集之前可能需要进行的任何清理操作。
清单1展示了一个非常简单的过滤器,该过滤器跟踪满足客户的Web请求所花费的大致时间:
清单1.一个过滤器类的实现
import javax.servlet.*;
import java.util.*;
import java.io.*;
public class TimeTrackFilter implements Filter {
private FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig)
throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter( ServletRequest request,
ServletResponse response, FilterChain chain )
throws IOException, ServletException {
Date startTime, endTime;
double totalTime;
startTime = new Date();
// Forward the request to the next resource in the chain
chain.doFilter(request, wrapper);
// -- Process the response -- \\
// Calculate the difference between the start time and end time
endTime = new Date();
totalTime = endTime.getTime() - startTime.getTime();
totalTime = totalTime / 1000; //Convert from milliseconds to seconds
StringWriter sw = new StringWriter();
PrintWriter writer = new PrintWriter(sw);
writer.println();
writer.println("===============");
writer.println("Total elapsed time is: " + totalTime + " seconds." );
writer.println("===============");
// Log the resulting string
writer.flush();
filterConfig.getServletContext().
log(sw.getBuffer().toString());
}
}
该过滤器的生命周期非常简单,但是无论如何,让我们一起探讨一下:
-
初始化
-
容器首次加载过滤器时,将调用
init()
方法。 该类在此方法中获取对FilterConfig
对象的引用。 我们的过滤器实际上不需要执行此操作,因为未使用任何初始化信息,但此处仅用于演示目的。
筛选
-
这是大部分过滤器寿命所消耗的地方。
容器调用
doFilter()
方法,并传递对该请求/响应链的ServletRequest
,ServletResponse
和FilterChain
对象的引用。 然后,筛选器将有机会处理请求,将处理传递到链中的下一个资源(通过在FilterChain
对象引用上调用doFilter()
),然后在处理控制权返回到筛选器时处理响应。
毁灭
-
容器在垃圾回收之前调用
destroy()
方法,以便可以执行所需的任何清理代码。
2.配置Servlet过滤器
过滤器通过web.xml文件中的两个XML标记声明。 <filter>
标记定义了<filter>
的名称,并声明了实现类和init()
参数。 <filter-mapping>
标记将过滤器与servlet或URL模式相关联。
清单2,来自web.xml文件的快照,展示了如何声明包含过滤器:
清单2.在web.xml中声明一个过滤器
<filter>
<filter-name>Page Request Timer</filter-name>
<filter-class>TimeTrackFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Page Request Timer</filter-name>
<servlet-name>Main Servlet</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>Main Servlet</servlet-name>
<servlet-class>MainServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Main Servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
在上面的代码示例中,声明了一个过滤器(“页面请求计时器”)并将其映射到Servlet(“主Servlet”)。 然后为该servlet定义一个映射,以便将每个请求(由通配符指示)发送到该servlet。 这是控制器组件的典型映射声明。 您应该注意这些声明的顺序,因为必须不偏离元素的这种顺序。
3.部署Servlet过滤器
事实是,将筛选器与Web应用程序一起部署绝对不涉及任何复杂性。 只需将过滤器类与其他Web组件类一起包括在内,并将web.xml(连同过滤器定义和过滤器映射声明一起)放到Web应用程序结构中,就像通常一样(在WEB-INF文件夹的根目录), servlet容器将处理那里的所有内容!
过滤器的多种用途
您在J2EE Web应用程序中使用过滤器的能力仅受您自己的创造力和应用程序设计能力的限制。 装饰过滤器模式或拦截器模式合适的任何地方,都可以使用过滤器。 过滤器最常用的一些用法如下:
- 日志记录 :过滤器收集有关通过系统的所有请求的信息,例如浏览器类型,一天中的时间,转发URL等,并将其记录下来。
- 性能 :过滤器在到达servlet和JSP页面之前,先对内容进行压缩,然后再将响应内容转换为压缩格式,然后再发送到客户端计算机。
- 安全性 :过滤器处理身份验证令牌的管理并适当限制对安全资源的访问,提示用户进行身份验证和/或将其传递给第三方进行身份验证。 过滤器甚至可以管理访问控制列表以提供除身份验证之外的授权。 将安全逻辑放入过滤器而不是servlet或JSP页面可提供极大的灵活性。 在开发过程中,可以关闭过滤器(从web.xml中注释掉)。 在生产中,将重新打开过滤器。 此外,可以添加多个过滤器,以根据需要提供更高级别的安全性,加密和不可否认服务。
- 会话处理 :使用会话处理代码乱糟糟的servlet和JSP页面可能会很麻烦。 使用过滤器来管理会话数据,可使您的网页专注于显示内容和委派处理,而无需担心会话管理的细节。
- XSLT转换 :无论是使用移动客户端还是基于XML的Web服务,在XML语法之间进行转换而无需将逻辑嵌入到应用程序中的能力绝对是无价的。
使过滤器适应MVC架构
模型-视图-控制器(MVC)体系结构是一种有效的设计,现已被作为最流行的Web应用程序框架(如Jakarta Struts和Turbine)中的首要设计方法而并入。 筛选器用于增强MVC体系结构的请求/响应处理流程。 无论请求/响应是在客户端与服务器之间还是在服务器上的其他组件之间,过滤器在流程中的应用都是相同的。 从MVC的角度来看,调度程序组件(包含在Controller组件中或与Controller组件一起工作)将请求转发到适当的应用程序组件进行处理。 这使得Controller层成为包含Servlet过滤器的最佳位置。 可以通过将过滤器放置在Controller组件本身的前面来将其应用于所有请求,或者通过将其放置在控制器/调度程序与Model and View组件之间来将其应用于单个Web组件。
MVC体系结构是广泛存在的,并且有据可查。 单击“ 相关主题”部分中的链接,以了解有关MVC体系结构中的MVC和Servlet实现的更多信息。
结论
尽管过滤器已经使用了仅两年时间,但它们已经将自己嵌入为任何敏捷的,面向对象的J2EE Web应用程序的关键组件。 在本文中,已向您介绍了如何使用Servlet过滤器。 我已经讨论了过滤器的高级设计,将当前的(2.4)规范与早期的(2.3)模型进行了比较,并描述了实现过滤器,在Web应用程序中声明该过滤器并通过以下方式进行部署的确切步骤:一个应用程序。 我还解释了Servlet过滤器的一些最常见用法,并谈到了过滤器如何适合传统的MVC体系结构。
这是J2EE探路者系列的最后一篇文章。 从年初开始,我们就着手仔细研究Enterprise JavaBean组件,询问何时使用它们以及何时过大才有意义。 然后,我们将重点转移到Web层,通过Servlet,JSP页面,JavaBean技术和Java Servlet API中的众多选项和功能来绘制路径。 与您一起浏览本系列文章真是一种荣幸。 我很喜欢撰写本系列文章,并且从您的反馈中得知,这对于您也是一个宝贵的过程。 感谢您参与本系列。 祝您好运,寻路愉快!
翻译自: https://www.ibm.com/developerworks/java/library/j-pj2ee10/index.html