9.1Filter API

过滤器中使用的接口包括FilterFilterConfigFilterChain

过滤器类必须实现javax.servlet.Filter接口。这个接口提供了3个方法:initdoFilterdestroy

当过滤器启动服务时,比如应用程序启动时,Servlet容器就会调用init方法。这个方法只调用一次,并且应该包含过滤器的初始化代码。签名如下:

void init(FilterConfig filterConfig)

每次调用与过滤器相关的资源时,Servlet容器都会调用Filter实例的doFilter方法。该方法会收到一个ServletRequestServletResponseFilteChain

下面是doFilter的签名:

void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)

doFilter方法实现中的最后一行代码应该是调用FilterChain中的doChain方法。

filterChain.doFilter(request, response)

一个资源可以与多个过滤器关联。FilterChain.doFilter()通常会引发调用链中的下一个过滤器被调用。在链中的最后一个过滤器中调用FilterChain.doFilter()会引发资源本身被调用。

如果你没有调用,那么程序的处理将会在这个地方出现停止,并且不会调用请求。

注意:doFilter方法是FilterChain接口中唯一的方法。它与Filter中的doFilter方法稍有不通。在FilterChain中,doFilter只有两个参数,而不是三个。

Filter中的最后一个声明周期方法是destroy,方法签名如下:

void destroy()

这个方法在过滤器即将终止服务之前,由servlet容器调用,一般发生在应用程序停止的时候。

除非一个过滤器类在部署描述符的多个filter元素中进行了声明,否则servlet容器将只给每一类过滤器创建一个实例。由于servlet/JSP应用程序通常是多用户的应用程序,因此可以同时通过多个线程访问一个过滤器实例,但你必须谨慎处理好多线程的问题。

 

9.2、过滤器的配置

FilterConfig访问ServletContext

ServletContext getServletContext()

获取它的名称:

java.lang.String getFilterName()

获取参数:

java.util.Enumeration<java.lang.String> getInitParameterNames()

java.lang.String getInitParameter(java.lang.String parameterName)

 

配置过滤器有两种方法:

1、利用注解@WebFilter

他有以下属性:

asyncSupported:指定过滤器是否支持异步操作模式

description:过滤器的描述

dispatcherTypes:应用过滤器的dispatcher类型

displayName:过滤器的显示名称

filterName:过滤器的名称

initParams:初始参数

largeIcon:过滤器的大图标名称

servletNames:适用于过滤器的Servlets名称

smallIcon:过滤器的小图标名称

urlPatterns:应用过滤器的URL模式

value:应用过滤器的URL模式

@WebFilter(filterName="DataCompressionFilter", urlPatterns={"/*"})
相当于web.xml中的

<filter>
  	<filter-name>DataCompressionFilter</filter-name>
  	<filter-class>
  		the fully-qualified name of the filter class
  	</filter-class>
  </filter>

再举个例子。下面的过滤器中设置了两个初始参数

@WebFilter(filterName="Security Filter", urlPatterns={"/*"}, 
	initParams = {
		@WebInitParam(name="frequency", value="1909"),
		@WebInitParam(name="resolution", value="1024")
	}
)
相当于

<filter>
  	<filter-name>Security Filter</filter-name>
  	<filter-class>filterclass</filter-class>
  	<init-param>
  		<param-name>frequency</param-name>
  		<param-value>1909</param-value>
  	</init-param>
  	<init-param>
  		<param-name>resolution</param-name>
  		<param-value>1024</param-value>
  	</init-param>
  </filter>
  <filter-mapping>
  	<filter-name>Security Filter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>

9.3、范例1:日志过滤器

来看一个例子,其中有一个简单的过滤器,用于在一个文本文件中记录请求URI。文本文件的名称可以通过一个初始参数进行配置。此外,日志中的每个入口前面都可以添加一个也是初始参数的预设字符串。从日志中可以推断出一些有价值的信息,例如应用程序中的拿一项资源最受欢迎,或者网站每天哪个时间段的访问量最大等。

LoggingFilter.class

package filter;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
@WebFilter(filterName = "LoggingFilter", urlPatterns = {"/*"}, 
	initParams = {
		@WebInitParam(name = "logFileName", value = "log.txt"), 
		@WebInitParam(name = "prefix", value = "URI: ")
	}
)
public class LoggingFilter implements Filter{
	private PrintWriter logger ;
	private String prefix ;

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		prefix = filterConfig.getInitParameter("prefix") ;
		String logFileName = filterConfig.getInitParameter("logFileName") ;
		//容器的路径
		String appPath = filterConfig.getServletContext().getRealPath("/") ;
		System.out.println("logFileName:" + logFileName);
		System.out.println(appPath);
		try {
			logger = new PrintWriter(new File(appPath, logFileName)) ;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			throw new ServletException(e.getMessage()) ;
		}
	}
	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain filterChain) throws IOException, ServletException {
		System.out.println("LoggingFilter.doFilter");
		HttpServletRequest httpServletRequest = (HttpServletRequest)request ;
		logger.println(new Date() + " " + prefix + httpServletRequest.getRequestURI());
		logger.flush(); 
		filterChain.doFilter(request, response);
	}
	
	@Override
	public void destroy() {
		System.out.println("destroying filter");
		if(logger != null){
			logger.close(); 
		}
	}
	

}

例如,我输入http://localhost:8089/filter/

在tomcat的项目目录下生成一个log.txt文件并记录刚才的访问。




9.4、范例2:图片保护过滤器

本例中的ImageProtectorFilter防止通过在浏览器的地址栏中直接输入图片URL来下载图片。只有在页面中单击图片的链接时,才会显示应用程序中的图片。过滤器通过查看HTTP标头referer的值进行工作。值为空表示当前请求没有相当的引用页,换句话说,该资源是直接输入其URL进行请求的。标头referer值非空的资源,将以原始页面作为引用页。

ImageProtectorFilter.java

package filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

@WebFilter(filterName = "ImageProtetorFilter", urlPatterns = {"*.png","*.jpg","*.gif"})
public class ImageProtectorFilter implements Filter{

	@Override
	public void destroy() {
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		System.out.println("ImageProtecorFilter");
		HttpServletRequest httpServletRequest = (HttpServletRequest)request ;
		String referer = httpServletRequest.getHeader("referer") ;
		System.out.println("referer:" + referer);
		if(referer != null){
			chain.doFilter(request, response);
		}else{
			throw new ServletException("Image not availabe") ;
		}
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}

}

image.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
  <head>
    <title>image</title>
  </head>
  
  <body>
    <img src="image/logo.png"/>
  </body>
</html>
当直接在浏览器中输入:http://localhost:8089/filter/image/logo.png

会出现错误: Image not availabe

通过访问浏览器:http://localhost:8089/filter/image.jsp可以得到图片


9.5、范例3:下载计数过滤器

本例中的下载计数过滤器可以计算某一个资源被下载了多少次。当你想要知道你的文档或者视频的受欢迎程度时,这个就很有帮助了。为了简便起见,这些数字会被保存在一个属性文件中,而不是保存在一个数据库中。资源URI做为属性文件的属性键。

DownloadCounterFilter.java

package filter;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
@WebFilter(filterName = "DownloadCounterFilter", urlPatterns = {"/*"})
public class DownloadCounterFilter implements Filter{
	ExecutorService executorService = Executors.newSingleThreadExecutor() ;
	Properties downloadLog ;
	File logFile ;
	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		executorService.shutdown();
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		// TODO Auto-generated method stub
		HttpServletRequest httpServletRequest = (HttpServletRequest)request ;
		final String uri = httpServletRequest.getRequestURI() ;
		executorService.execute(new Runnable(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				String property = downloadLog.getProperty(uri) ;
				if(property == null){
					downloadLog.setProperty(uri, "1") ;
				}else{
					int count = 0 ;
					try {
						count = Integer.parseInt(property) ;
					} catch (NumberFormatException e) {
						e.printStackTrace();
					}
					count ++ ;
					downloadLog.setProperty(uri, Integer.toString(count)) ;
				}
				try {
					downloadLog.store(new FileWriter(logFile), "");
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});
		chain.doFilter(request, response);
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// TODO Auto-generated method stub
		System.out.println("DownloadCounterFilter");
		String appPath = filterConfig.getServletContext().getRealPath("/") ;
		logFile = new File(appPath, "downloadLog.txt") ;
		if(!logFile.exists()){
			try {
				logFile.createNewFile() ;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		downloadLog = new Properties() ;
		try {
			downloadLog.load(new FileReader(logFile));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

9.6、过滤器的顺序

如果多个过滤器应用于同一个资源,那么调用顺序就很重要,必须用部署描述符管理应该先调用哪一个过滤器。加入filter1必须在filter2之前调用,那么在部署描述符中,Filter1的声明就要放在Filter2的声明之前。



Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐