web过滤器
过滤器是指拦截请求,并对传给被请求资源的ServletRequest或ServletResponse进行处理的一个对象。
过滤器可用于登录、加密和解密、对话检查、图片转换等待。过滤器可以配置拦截一个或者多个资源
1.Filter API
过滤器必须实现javax.servret.Filter接口,这个接口暴露三个生命周期方法:init,doFilter,destroy
当过滤器启动服务时,Servlet容器就会调用init方法。这个方法指调用一次
void init(FilterConfig filterConfig)
filterConfig可用于获取ServletContext对象,或者获取初始化属性(getInitParameter)
doFilter方法时过滤器核心
void doFilter(ServletRequest request ,ServletResponse response,FilterChain chain)
可以在ServletRequest 中添加属性,或者在ServletResponse添加一个标头
也可以获取HttpServletRequest对象
doFilter方法实现中的最后一行代码应该时调用FilterChain中的doFilter(request,response)方法
表示放行,通常会引发下一个过滤器被调用。
void destroy()
这个方法在过滤器即将终止服务之前,有servlet容器调用
2.过滤器的配置
确定要拦截哪些资源 (urlPatterns value)
要传给init方法的启动初始值(initParams) 可通过getParameterNames 和getParameter方法来获取
给过滤器七个名字(filterName)
可以通过@webFilter注解 和部署描述符中声明
下面通过三个实例来带你了解神秘的过滤器
实例一:日志过滤器
通过一个过滤器,用于在一个文本文件中记录请求的URI。从日志中 可以推断出一些有价值的信息,例如
应用程序中哪一项资源最受欢迎,获知网站每天哪个时间段的访问量最多
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 String prefix;
private PrintWriter logger;
@Override
public void destroy() {
// TODO Auto-generated method stub
System.out.println("destroying filter");
if(logger!=null){
logger.close();
}
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
// TODO Auto-generated method stub
System.out.println("LoggingFilter.doFilter");
HttpServletRequest httpServletRequest=(HttpServletRequest) arg0;
logger.println(new Date()+" "+prefix+
httpServletRequest.getRequestURL());
logger.flush();
arg2.doFilter(arg0, arg1);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
prefix=arg0.getInitParameter("prefix");
String logFileName=arg0.getInitParameter("logFileName");
String appPath=arg0.getServletContext().getRealPath("/");//利用FilterConfig获取servletContext对象的getRealPath方法来获取应用程序的绝对路径
System.out.println("logFileName:"+logFileName);
try{
logger=new PrintWriter(new File(appPath,logFileName));
}catch(FileNotFoundException e){
e.printStackTrace();
throw new ServletException(e.getMessage());
}
}
}
实例2:图片保护过滤器
本例防止通过在浏览器的地址栏直接输入url来下载图片。只有当在页面中点击图片的链接时,图片才会下载
过滤器通过查看HTTP标头referer的值来判断,只有当标头不为空时,才放行
标头为空时表示请求没有相当的引用页
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="ImageProtectorFilter",urlPatterns={"*.png","*.jpg","*.gif"})
public class ImageProtectorFilter implements Filter {
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
// TODO Auto-generated method stub
System.out.println("imageProtextFilter doFilter");
HttpServletRequest httpServletRequest=(HttpServletRequest) arg0;
String referrer=httpServletRequest.getHeader("referer");
System.out.println("referer:"+referrer);
if(referrer!=null){
arg2.doFilter(arg0, arg1);
}else{
throw new ServletException("Image not available");
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
实例3:下载计数过滤器
本例的下载计数过滤器可以计算某一个资源被下载了多少次。当你想要知道你的视频的受欢迎程度或者文档的下载次数
这个就很有帮助,将这些数据保存在一个属性文件中,并且多个线程可以同时访问一个过滤器,因此需要一个线程安全性
的问题需要解决,这个例子通过利用Queue和Executor来解决这个线程安全性问题,将所有进来的请求都在一个线程的
Executor的队列中放置一个任务。放置任务为异步操作,比较快。Executor每次从队列中取出一个项目,消除了多线程
访问该属性文件的可能性
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();//ExecutorService是Executor的一个子类
Properties downloadLog; //Properties为HashTable的子类,线程安全
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){
}
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 init");
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 (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
如果多个过滤器应用于一个资源,必须用部署描述符管理应该先调用哪个一个过滤器
filter1
class name