你能够在两种状况下使用本文:
·学习过滤器的功用,
·做为你写过滤器时的辅助。
我将从几个简单的例子开始而后继续更多高级的过滤器。最后,我将向你介绍我为了支持多路请求而写的一个文件上传过滤器。
Servlet 过滤器
也许你还不熟悉状况,一个过滤器是一个能够传送请求或修改响应的对象。过滤器并非
servlet,他们并不实际建立一个请求。他们是请求到达一个servlet前的预处理程序,和/或响应离开servlet后的后处理程序。就像你将在后面的例子中看到的,一个过滤器可以:
·在一个
servlet被调用前截获该调用
·在一个
servlet被调用前检查请求
·修改在实际请求中提供了可定制请求对象的请求头和请求数据
·修改在实际响应中提供了可定制响应对象的响应头和响应数据
·在一个
servlet被调用以后截获该调用
一个过滤器以做用于一个或一组servlet,零个或多个过滤器能过滤一个或多个servlet。一个过滤器须要实现java.servlet.Filter接口,并定义它的三个方法:
1.
void init(FilterConfig config) throws ServletException:在过滤器执行service前被调用,以设置过滤器的配置对象。
2.
void destroy();在过滤器执行service后被调用。
3.
Void doFilter(ServletRequest req,ServletResponse res,FilterChain chain) throws IOException,ServletException;执行实际的过滤工做。
服务器调用一次
init(FilterConfig)觉得服务准备过滤器,而后在请求须要使用过滤器的任什么时候候调用doFilter()。FilterConfig接口检索过滤器名、初始化参数以及活动的servlet上下文。服务器调用destory()以指出过滤器已结束服务。过滤器的生命周期和servelt的生命周期很是类似 ——在Servlet API 2.3 最终发布稿2号 中最近改变的。先前得用setFilterConfig(FilterConfig)方法来设置生命周期。
在
doFilter()方法中,每一个过滤器都接受当前的请求和响应,而FilterChain包含的过滤器则仍然必须被处理。doFilter()方法中,过滤器能够对请求和响应作它想作的一切。(就如我将在后面讨论的那样,经过调用他们的方法收集数据,或者给对象添加新的行为。)过滤器调用
chain.doFilter()将控制权传送给下一个过滤器。当这个调用返回后,过滤器能够在它的doFilter()方法的最后对响应作些其余的工做;例如,它能记录响应的信息。若是过滤器想要终止请求的处理或得对响应的彻底控制,则他能够不调用下一个过滤器。
按部就班
若是想要真正理解过滤器,则应该看它们在实际中的应用。咱们将看到的第一个过滤器是简单而有用的,它记录了全部请求的持续时间。在
Tomcat 4.0发布中被命名为ExampleFilter。代码以下:
java 代码
- import java.io.*;
- import javax.servlet.*;
- import javax.servlet.http.*;
-
- public class TimerFilter implements Filter {
-
- private FilterConfig config = null;
-
- public void init(FilterConfig config) throws ServletException {
- this.config = config;
- }
-
- public void destroy() {
- config = null;
- }
-
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException {
- long before = System.currentTimeMillis();
- chain.doFilter(request, response);
- long after = System.currentTimeMillis();
-
- String name = "";
- if (request instanceof HttpServletRequest) {
- name = ((HttpServletRequest)request).getRequestURI();
- }
- config.getServletContext().log(name + ": " + (after - before) + "ms");
- }
- }
当服务器调用
init()时,过滤器用config变量来保存配置类的引用,这将在后面的doFilter()方法中被使用以更改ServletContext。当调用doFilter()时,过滤器计算请求发出到该请求执行完毕之间的时间。该过滤器很好的演示了请求以前和以后的处理。注意doFilter()方法的参数并非HTTP对象,所以要调用HTTP专用的getRequestURI()方法时必须将request转化为HttpServletRequest类型。
使用此过滤器,你还必须在
web.xml文件中用<filter></filter>标签部署它,见下:
- <filter>
- <filter-name>timerFilterfilter-name>
- <filter-class>TimerFilterfilter-class>
- /filter>
这将通知服务器一个叫
timerFiter的过滤器是从TimerFiter类实现的。你可使用肯定的URL模式或使用<filter-mapping></filter-mapping>标签命名的servelt 来注册一个过滤器,如:
- <filter-mapping>
- <filter-name>timerFilterfilter-name>
- <url-pattern>/*url-pattern>
- filter-mapping>
这种配置使过滤器操做全部对服务器的请求(静态或动态),正是咱们须要的计时过滤器。若是你链接一个简单的页面,记录输出可能以下:
2001-05-25 00:14:11 /timer/index.html: 10ms
在
Tomcat 4.0 beta 5中,你能够在server_root/logs/下找到该记录文件。
此过滤器的
WAR文件今后下载:
谁在你的网站上?他们在作什么?
咱们下一个过滤器是由
OpenSymphony成员写的clickstream过滤器。这个过滤器跟踪用户请求(好比:点击)和请求队列(好比:点击流)以向网络管理员显示谁在她的网站上以及每一个用户正在访问那个页面。这是个使用LGPL的开源库。
在
clickstream包中你将发现一个捕获请求信息的ClickstreamFilter类,一个像操做结构同样的Clickstream类以保存数据,以及一个保存会话和上下文事件的ClickstreamLogger类以将全部东西组合在一块儿。还有个BotChecker类用来肯定客户端是不是一个机器人(简单的逻辑,像“他们是不是从robots.txt来的请求?”)。该包中提供了一个clickstreams.jsp摘要页面和一个viewstream.jsp详细页面来查看数据。
咱们先看
ClickstreamFilter类。全部的这些例子都作了些轻微的修改以格式化并修改了些可移植性问题,这我将在后面将到。
- import java.io.IOException;
- import javax.servlet.*;
- import javax.servlet.http.*;
-
- public class ClickstreamFilter implements Filter {
- protected FilterConfig filterConfig;
- private final static String FILTER_APPLIED = "_clickstream_filter_applied";
-
- public void init(FilterConfig config) throws ServletException {
- this.filterConfig = filterConfig;
- }
-
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException {
- // 确保该过滤器在每次请求中只被使用一次
- if (request.getAttribute(FILTER_APPLIED) == null) {
- request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
- HttpSession session = ((HttpServletRequest)request).getSession();
- Clickstream stream = (Clickstream)session.getAttribute("clickstream");
- stream.addRequest(((HttpServletRequest)request));
- }
-
- // 传递请求
- chain.doFilter(request, response);
- }
-
- public void destroy() { }
- }
doFilter()方法取得用户的session,从中获取Clickstream,并将当前请求数据加到Clickstream中。其中使用了一个特殊的FILTER_APPLIED标记属性来标注此过滤器是否已经被当前请求使用(可能会在请求调度中发生)而且忽略全部其余的过滤器行为。你可能疑惑过滤器是怎么知道当前session中有clickstream属性。那是由于ClickstreamLogger在会话一开始时就已经设置了它。ClickstreamLogger代码:
- import java.util.*;
- import javax.servlet.*;
- import javax.servlet.http.*;
-
- public class ClickstreamLogger implements ServletContextListener,
- HttpSessionListener {
- Map clickstreams = new HashMap();
-
- public ClickstreamLogger() { }
-
- public void contextInitialized(ServletContextEvent sce) {
- sce.getServletContext().setAttribute("clickstreams", clickstreams);
- }
-
- public void contextDestroyed(ServletContextEvent sce) {
- sce.getServletContext().setAttribute("clickstreams", null);
- }
-
- public void sessionCreated(HttpSessionEvent hse) {
- HttpSession session = hse.getSession();
- Clickstream clickstream = new Clickstream();
- session.setAttribute("clickstream", clickstream);
- clickstreams.put(session.getId(), clickstream);
- }
-
- public void sessionDestroyed(HttpSessionEvent hse) {
- HttpSession session = hse.getSession();
- Clickstream stream = (Clickstream)session.getAttribute("clickstream");
- clickstreams.remove(session.getId());
- }
- }
logger(记录器)获取应用事件并将使用他们将全部东西帮定在一块儿。当context建立中,logger在context中放置了一个共享的流map。这使得clickstream.jsp页面知道当前活动的是哪一个流。而在context销毁中,logger则移除此map。当一个新访问者建立一个新的会话时,logger将一个新的Clickstream实例放入此会话中并将此Clickstream加入到中心流map中。在会话销毁时,由logger从中心map中移除这个流。
下面的
web.xml部署描述片断将全部东西写在一块:
- <filter>
- <filter-name>clickstreamFilterfilter-name>
- <filter-class>ClickstreamFilterfilter-class>
- filter>
- <filter-mapping>
- <filter-name>clickstreamFilterfilter-name>
- <url-pattern>*.jspurl-pattern>
- filter-mapping>
- <filter-mapping>
- <filter-name>clickstreamFilterfilter-name>
- <url-pattern>*.htmlurl-pattern>
- filter-mapping>
- <listener>
- <listener-class>ClickstreamLoggerlistener-class>
- listener>