三個例項帶你走進web過濾器Filter API
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();
}
}
}
過濾器的順序
如果多個過濾器應用於一個資源,必須用部署描述符管理應該先呼叫哪個一個過濾器
<filter>
<filter-name> filter1</filter-name>
<filter-class>class name</filter-class>
</filter>
<filter>
<filter-name> filter2</filter-name>
<filter-class>class name</filter-class>
</filter>