Java過濾器filter_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
Filter過濾器技術(shù)。通過過濾器,可以對(duì)來自客戶端的請求進(jìn)行攔截,進(jìn)行預(yù)處理或者對(duì)最終響應(yīng)給客戶端的數(shù)據(jù)進(jìn)行處理后再輸出。
要想使用Filter過濾器,非常簡單,只要實(shí)現(xiàn)Servlet API中的Filter接口即可,同時(shí)在該web應(yīng)用【W(wǎng)EB-INF】目錄下的web.xml文件中配置<filter>和<filter-mapping>兩個(gè)標(biāo)簽。其中可以根據(jù)配置指定過濾的頁面或者Servlet。
也就是說我們在web工程中光光寫Filter過濾器的Java代碼是不會(huì)起作用的,要在web.xml文件中對(duì)過濾器進(jìn)行注冊和映射,在學(xué)習(xí)Filter之前我們先來學(xué)習(xí)如何注冊和映射
關(guān)于注冊:
需要在web.xml文件中配置<filter>標(biāo)簽,這還不夠,<filter>標(biāo)簽下的<filter-name>與<filter-class>是必須要填的內(nèi)容。
<filter>標(biāo)簽中有如下子元素:
- <description>用于描述該標(biāo)簽,非必須;
- <filter-name>為過濾器指定一個(gè)名稱,必須的
- <filter-class>指定該過濾器使用的web工程中的哪一個(gè)filter類,包含包名與類名,必須的;
- <init-param>為過濾器的初始化提供參數(shù),非必須,后面有例子。
關(guān)于映射:
需要在web.xml文件中配置<filter-mapping>標(biāo)簽,這還不夠,<filter-mapping>標(biāo)簽下的<filter-name>以及<url-pattern>或<servlet-name>之一是必須的。
<filter-mapping>標(biāo)簽中有如下子元素:
- <filter-name>設(shè)置要映射過濾器的名稱,該名稱必須同<filter>標(biāo)簽下的<filter-name>的值一致。
- <url-pattern>設(shè)置過濾器要攔截過濾的請求路徑,例如“/*”則表示對(duì)該web應(yīng)用下所有的請求都進(jìn)行攔截過濾。
- <servlet-name>如果只要攔截過濾訪問某個(gè)Servlet,就可以使用該標(biāo)簽來替代<url-pattern>。
- <dispatcher>設(shè)置攔截過濾客戶端請求的方式,有REQUEST,INCLUDE,F(xiàn)ORWARD,ERROR四種(請注意均為大寫)。非必須則默認(rèn)為REQUEST,使用多個(gè)<dispatcher>標(biāo)簽來設(shè)置多種請求方式。
關(guān)于<dispathcer>的四種方式,這里再簡單的介紹一下:
- REQUEST:當(dāng)用戶直接訪問我們的資源時(shí),這時(shí)我們設(shè)置的過濾器就會(huì)進(jìn)行攔截。但如果以轉(zhuǎn)發(fā)和包含方式訪問資源,那么該過濾器則不會(huì)被調(diào)用。
- INCLUDE:當(dāng)使用RequestDispatch的include方法請求時(shí),該過濾器會(huì)被調(diào)用。
- FORWARD:當(dāng)使用RequestDispatch的forward方法時(shí)請求資源時(shí),該過濾器會(huì)被調(diào)用,尤其是在MVC設(shè)計(jì)模式下,JSP都被保護(hù)起來,必須要通過Servlet進(jìn)行轉(zhuǎn)發(fā)才能訪問JSP,那么該過濾器就是在Servlet轉(zhuǎn)發(fā)到JSP這個(gè)過程中被執(zhí)行。
- ERROR:當(dāng)請求是通過錯(cuò)誤異常進(jìn)行跳轉(zhuǎn)時(shí)就會(huì)調(diào)用該過濾器。
一個(gè)簡單的對(duì)過濾器的注冊和映射的示例:
<filter> <filter-name>FilterDemo1</filter-name> <filter-class>com.bjpowernode.web.filter.FilterDemo1</filter-class> </filter> <filter-mapping> <filter-name>FilterDemo1</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping>
在Servlet API 中關(guān)于Filter舉例了使用過濾器能用來做些什么:

這里我也說明下平時(shí)Filter能在哪些方面會(huì)被經(jīng)常用到:
① Filter可以作用在請求資源執(zhí)行之前,進(jìn)行權(quán)限檢查,檢查用戶是否有權(quán)限,如有權(quán)限則放行請求;如果沒有,則拒絕訪問。
② Filter可以作用在請求資源執(zhí)行之前,對(duì)Request和Response對(duì)象進(jìn)行預(yù)處理操作,從而實(shí)現(xiàn)一些web應(yīng)用的全局性設(shè)置,比如解決中文亂碼問題。
③ Filter可以作用在最終響應(yīng)輸出之前,對(duì)輸出Response對(duì)象中的數(shù)據(jù)進(jìn)行處理,例如將輸出的數(shù)據(jù)進(jìn)行壓縮。
Filter只有3個(gè)方法:

其中destroy()方法和init(…)方法是生命周期方法,因?yàn)檫^濾器無論如何都要在請求任何資源之前進(jìn)行,所以任何Web應(yīng)用在部署的時(shí)候,服務(wù)器就會(huì)調(diào)用Filter過濾器的init方法進(jìn)行初始化,而關(guān)于過濾器的銷毀,則是將該過濾器移除或者服務(wù)器關(guān)閉就會(huì)執(zhí)行destory方法。
而我們通常要使用過濾器處理請求,則重點(diǎn)在于doFilter(…)方法。當(dāng)請求要經(jīng)過一個(gè)過濾器的時(shí)候,就會(huì)由服務(wù)器調(diào)用doFilter方法。
我們先來看看一個(gè)帶有過濾器Filter的web應(yīng)用的請求和響應(yīng)流程:

記?。簭恼埱蟮巾憫?yīng)這個(gè)流程會(huì)經(jīng)過Filter對(duì)象兩次!
在doFilter這一個(gè)方法中就可以對(duì)著兩次經(jīng)過的過程進(jìn)行處理,那么這里就有一個(gè)問題了,如果能通過過濾器,那么就到過濾器后面了,貌似應(yīng)該是執(zhí)行完doFilter方法了,而服務(wù)器的響應(yīng)又經(jīng)過過濾器,難道又要執(zhí)行doFilter方法一次?但是這個(gè)方法里面的代碼不是也有處理最開始請求的嗎?
這就跟doFilter方法中的第三個(gè)參數(shù)FilterChain有關(guān)了,F(xiàn)ilterChain對(duì)象是過濾器鏈,這個(gè)我們稍后會(huì)介紹。在FilterChain對(duì)象中只有一個(gè)方法:

=也是叫doFilter方法(千萬別和Filter接口的doFilter方法弄混了)。簡單的說下這個(gè)方法,只要調(diào)用了這個(gè)方法,就會(huì)將請求交給后面一個(gè)Filter進(jìn)行過濾(一個(gè)Web應(yīng)用中可以有多個(gè)Filter),如果該Filter是最后一個(gè),那么調(diào)用該方法則將執(zhí)行請求,也就是到我們的應(yīng)用中獲取資源。
因此從請求到響應(yīng)這個(gè)流程經(jīng)過Filter的兩次處理分別是在FilterChain.doFilter方法的前面和后面!如下圖所示:

那么下面我們就先以一個(gè)簡單的例子來熟悉下Filter吧:
例1:
創(chuàng)建web工程FilterLearning,創(chuàng)建一個(gè)FilterDemo1類,同時(shí)這個(gè)類要實(shí)現(xiàn)javax.servlet.Filter接口。如下代碼:
public class FilterDemo implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("hello filter");
}
//此處省略init方法和destory方法
}
寫好Filter的Java代碼還沒完,還要在web應(yīng)用下的web.xml文件中配置如下信息:
<filter>
<filter-name>FilterDemo1</filter-name>
<filter-class>com.bjpowernode.web.filter.FilterDemo</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注意:因?yàn)槲以?lt;url-pattern>中配置為“/*”,則訪問我wen應(yīng)用中任何資源都會(huì)經(jīng)過該Filter過濾器。如果只想對(duì)于index.jsp主頁的請求進(jìn)行過濾,可以設(shè)為<url-pattern>/index.jsp</url-pattern>。
我們在index.jsp中簡單的使用JSP腳本來演示如果有請求來就輸入一段文本到控制臺(tái)上:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title>index</title>
</head>
<body>
<%
System.out.println("Long live SD !");
%>
</body>
</html>
接下來將該web應(yīng)用部署到服務(wù)器中,我們就訪問index.jsp,以下是我們訪問了index.jsp后控制臺(tái)的情況:

首先,我們可以保證在我們訪問index.jsp后這個(gè)請求確實(shí)經(jīng)過了Filter過濾器,但是我們的請求好像就只到過濾器而沒有到我們真正需要的資源index.jsp?這是因?yàn)槲覀儧]有在Filter的doFilter方法中調(diào)用過濾器鏈FilterChain對(duì)象的doFilter方法,自然無法將請求繼續(xù)往后面?zhèn)鬟f。我們將在例2中修改。
例2:
我們將例1中的FilterDemo1類進(jìn)行修改,使其能訪問到我們所需要的資源,很簡單,在doFilter的方法中添加過濾器鏈FilterChain對(duì)象的doFilter方法即可:
public class FilterDemo implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("hello filter");
chain.doFilter(request, response);
}
//此處省略init方法和destory方法
}
其他如web.xml中的配置和index.jsp中的代碼保持不變,現(xiàn)在我們再來訪問下該web應(yīng)用中的index.jsp,并觀察控制臺(tái):

可以看到我們的請求經(jīng)過過濾器,執(zhí)行了過濾器的一段代碼(System.out.println(“hello filter”)),然后將請求繼續(xù)執(zhí)行!正是因?yàn)镕ilterChain.doFilter方法才使我們通過過濾器繼續(xù)向后尋找我們所需的資源。
那么還記得我們之前說過的從請求到響應(yīng)會(huì)經(jīng)過兩次過濾器嗎,是的在獲取了我們所需的資源后還會(huì)到過濾器一趟,而至于這時(shí)候是否將響應(yīng)再做處理取決于過濾器鏈FilterChain.doFilter方法后面還是否有代碼。我們將在例3中完整的展現(xiàn)從請求到響應(yīng)經(jīng)過過濾器兩次的流程。
例3:
我們將例2中的FilterDemo1類進(jìn)行修改,只要在FilterChain.doFilter方法后面添加代碼,就是第二次(即響應(yīng))經(jīng)過過濾器所要執(zhí)行的處理:
public class FilterDemo implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("hello filter");
chain.doFilter(request, response);
System.out.println("goodbye filter");
}
//此處省略init方法和destory方法
}
其他如web.xml中的配置和index.jsp中的代碼保持不變,現(xiàn)在我們再來訪問下該web應(yīng)用中的index.jsp,并觀察控制臺(tái):

這個(gè)結(jié)果證明了從請求到響應(yīng)確實(shí)經(jīng)過過濾器兩次,同時(shí)也說明了在Filter的doFilter方法中“過濾——取資源——再過濾”執(zhí)行的順序。
現(xiàn)在我們再回到Filter接口的init方法,我們可以看到在這個(gè)方法內(nèi)有一個(gè)參數(shù)FilterConfig,這個(gè)是由服務(wù)器傳給我們的對(duì)象。如果我們在web.xml文件中配置了過濾器的初始化參數(shù),就可以通過該FilterConfig對(duì)象來在代碼中獲取使用。
這個(gè)過濾器參數(shù)的初始化配置可以在<filter>標(biāo)簽中配置<init-param>,并在這個(gè)<init-param>標(biāo)簽下再配置<param-name>和<param-value>。
FilterConfig有如下方法:

當(dāng)然如果我們是要獲取配置的初始化參數(shù)則只需關(guān)注getInitParameter方法或getInitParameterNames方法。
一般來說我們可以在init方法中獲取配置初始化參數(shù)并進(jìn)行處理;也可以通過對(duì)象引用將FilterConfig對(duì)象在doFilter方法中處理參數(shù),如例4所示。
例4:
在web.xml文件中配置過濾器和初始化參數(shù):
<filter>
<filter-name>FilterDemo1</filter-name>
<filter-class>com.bjpowernode.web.filter.FilterDemo1</filter-class>
<init-param>
<param-name>Love</param-name>
<param-value>LRR</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在Java中編寫Filter接口的實(shí)現(xiàn)類FilterDemo1:
public class FilterDemo implements Filter {
private FilterConfig filterConfig ;
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String value = filterConfig.getInitParameter("Love");
System.out.println(value);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
//此處省略destroy方法
}
因?yàn)榕渲玫脑?,所以我們隨便訪問個(gè)資源都可以經(jīng)過該過濾器,那么就訪問index.jsp好了,看看控制臺(tái)的結(jié)果:

正如我們在web.xml文件所配置的初始化參數(shù)一樣。
上面介紹的都是只有一個(gè)Filter過濾器的情況下,有時(shí)候我們會(huì)因?yàn)橐^濾的功能不同添加多個(gè)過濾器,這就有一個(gè)順序的問題了,尤其是從取得資源后再返回到過濾器的順序。下面這張圖就能很清晰的看到我們要注意的順序了:

例5:
來寫兩個(gè)Filter來說明下從請求到響應(yīng)過濾器的處理順序。
創(chuàng)建一個(gè)web工程,創(chuàng)建一個(gè)FilterDemo1類,同時(shí)這個(gè)類要實(shí)現(xiàn)javax.servlet.Filter接口。如下代碼:
public class FilterDemo1 implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("Hello filter 1");
chain.doFilter(request, response);
System.out.println("Goodbye filter 1");
}
//此處省略init方法和destroy方法
}
創(chuàng)建第二個(gè)Filter接口實(shí)現(xiàn)類FilterDemo2,代碼如下:
public class FilterDemo2 implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("Hello filter 2");
chain.doFilter(request, response);
System.out.println("Goodbye filter 2");
}
//此處省略init方法和destroy方法
}
過濾器要想能被服務(wù)器調(diào)用,還必須要在該web工程下的web.xml中配置過濾器及其映射,而這個(gè)配置的順序就是影響多個(gè)過濾器工作先后的順序:
<filter>
<filter-name>FilterDemo1</filter-name>
<filter-class>com.bjpowernode.web.filter.FilterDemo1</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>FilterDemo2</filter-name>
<filter-class>com.bjpowernode.web.filter.FilterDemo2</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
而我們要訪問的資源文件就以index.jsp為例好了,那么我們用一段JSP腳本通過在控制臺(tái)打印來驗(yàn)證過濾器工作的順序過程:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title>index</title>
</head>
<body>
<%
System.out.println("Long live SD !");
%>
</body>
</html>
現(xiàn)在啟動(dòng)服務(wù)器,部署該工程,通過訪問index.jsp來看看控制臺(tái)情況:

相關(guān)文章
SpringBoot調(diào)用Poi-tl實(shí)現(xiàn)渲染數(shù)據(jù)并生成Word文檔
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何調(diào)用Poi-tl實(shí)現(xiàn)渲染數(shù)據(jù)并生成Word文檔,文中的示例代碼講解詳細(xì),有需要的小伙伴可以了解下2023-09-09
RestTemplate報(bào)錯(cuò)400 Bad Request的解決方案
在使用Spring Boot時(shí),若直接通過@Autowired注入RestTemplate可能會(huì)遇到400BadRequest錯(cuò)誤,原因在于Spring Boot官方文檔指出,由于RestTemplate實(shí)例通常需要在使用前進(jìn)行定制,因此Spring Boot不會(huì)自動(dòng)配置單個(gè)RestTemplate Bean2024-11-11
Java解除文件占用即Dom4j操作后實(shí)現(xiàn)xml關(guān)流
這篇文章主要介紹了Java解除文件占用即Dom4j操作后實(shí)現(xiàn)xml關(guān)流,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
解決idea默認(rèn)帶的equals和hashcode引起的bug
這篇文章主要介紹了解決idea默認(rèn)帶的equals和hashcode引起的bug,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Spring中@PostConstruct的實(shí)現(xiàn)方法
大多數(shù)java程序員都使用過@PostConstruct注解,它的作用就是在Bean初始化完成后執(zhí)行,相當(dāng)于我們常說的init()方法,但是我們看@PostConstruct只有單單的一個(gè)注解,它到底是如何實(shí)現(xiàn)在Bean初始化完成后就被調(diào)用的呢,本文將詳細(xì)給大家介紹一下2023-06-06

