java web 過濾器跟攔截器的區別和使用
注:文章整理自知乎大牛以及百度網友(電腦網路分類達人 呂明),特此感謝!
一、過濾器
1.什麼是過濾器?
過濾器是一個程式,它先於與之相關的servlet或JSP頁面執行在伺服器上。過濾器可附加到一個或多個servlet或JSP頁面上,並且可以檢查進入這些資源的請求資訊。在這之後,過濾器可以作如下的選擇:
①以常規的方式呼叫資源(即,呼叫servlet或JSP頁面)。
②利用修改過的請求資訊呼叫資源。
③呼叫資源,但在傳送響應到客戶機前對其進行修改。
④阻止該資源呼叫,代之以轉到其他的資源,返回一個特定的狀態程式碼或生成替換輸出。
2.Servlet過濾器的基本原理:
在Servlet作為過濾器使用時,它可以對客戶的請求進行處理。處理完成後,它會交給下一個過濾器處理,這樣,客戶的請求在過濾鏈裡逐個處理,直到請求傳送到目標為止。例如,某網站裡有提交“修改的註冊資訊”的網頁,當用戶填寫完修改資訊並提交後,伺服器在進行處理時需要做兩項工作:判斷客戶端的會話是否有效;對提交的資料進行統一編碼。這兩項工作可以在由兩個過濾器組成的過濾鏈裡進行處理。當過濾器處理成功後,把提交的資料傳送到最終目標;如果過濾器處理不成功,將把檢視派發到指定的錯誤頁面。
3.過濾器:只想要在一堆東西里面選個B:
4.示例程式碼
在web.xml裡面配置自定義的過濾器
<filter >
<filter-name>Redirect Filter</filter-name>
<filter-class>com.xx.filter.RedirectFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Redirect Filter</filter-name>
<url-pattern>/xx/xx/*</url-pattern>
</filter-mapping>
如何編寫自定義的過濾器
public class RedirectFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
// 獲取URL
Long startTime = null;
if (log.isDebugEnabled())
{
startTime = System.currentTimeMillis();
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
String url = httpRequest.getRequestURL().toString();
if (url == null || url.trim().length() == 0) {
return;
}
if (url.indexOf(luceneCreateMapping) != -1
|| url.indexOf(luceneSearchMapping) != -1) {
doFilterForxxx(request, response, url);
} else {
doxxxx(request, response, url);
}
if (log.isDebugEnabled())
{
long endTime = System.currentTimeMillis();
Thread currentThread = Thread.currentThread();
String threadName = currentThread.getName();
log.debug("[" + threadName + "]" + "< "
+ this.getClass().getName() + " " + url + " "
+ (endTime - startTime) + " ms");
}
// 啟用下一個Filter
filterChain.doFilter(request, response);
}
}
示例2
// 填充100個帶有隨機字母標籤的球
List<String> array = new ArrayList<>();
Random r = new Random();
String[] balls = new String[]{"A", "B", "C"};
for (int i = 0; i < 100; i++)
array.add(balls[r.nextInt(3)]);
// 只拿出B的來。不明白的自行學習Java 8
array = array.stream().filter(ball -> ball.equals("B")).collect(Collectors.toList());
作者:知乎使用者
連結:https://www.zhihu.com/question/35225845/answer/61876681
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
二、攔截器
1.什麼是攔截器?
攔截器,在AOP(Aspect-Oriented Programming)中用於在某個方法或欄位被訪問之前,進行攔截然後在之前或之後加入某些操作。攔截是AOP的一種實現策略。
在Webwork的中文文件的解釋為——攔截器是動態攔截Action呼叫的物件。它提供了一種機制可以使開發者可以定義在一個action執行的前後執行的程式碼,也可以在一個action執行前阻止其執行。同時也是提供了一種可以提取action中可重用的部分的方式。
談到攔截器,還有一個詞大家應該知道——攔截器鏈(Interceptor Chain,在Struts 2中稱為攔截器棧 Interceptor Stack)。攔截器鏈就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或欄位時,攔截器鏈中的攔截器就會按其之前定義的順序被呼叫。
2.攔截器的實現原理:
大部分時候,攔截器方法都是通過代理的方式來呼叫的。Struts 2的攔截器實現相對簡單。當請求到達Struts 2的ServletDispatcher時,Struts 2會查詢配置檔案,並根據其配置例項化相對的攔截器物件,然後串成一個列表(list),最後一個一個地呼叫列表中的攔截器。
3.攔截器:把水流變小點,把魚都攔住!順便發個電:
4.示例程式碼
在xml檔案中如何定義攔截器
<interceptors>
<interceptor name="filterIPInterceptor" class="com.xxxx.web.FilterIPActionInterceptor" />
<interceptor-stack name="filterIPStack">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="filterIPInterceptor" />
</interceptor-stack>
</interceptors>
怎麼編寫自定義攔截器
public class FilterIPActionInterceptor extends AbstractInterceptor
{
/** 日誌控制. */
private final Log log = LogFactory.getLog(getClass());
/**
* @see com.opensymphony.xwork2.interceptor.AbstractInterceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
*/
@Override
@SuppressWarnings("unchecked")
public String intercept(ActionInvocation invocation) throws Exception
{
String result = null;
// 獲得當前方法名.
String methodName = invocation.getInvocationContext().getName();
String currIp = null;
try
{
if (invocation.getAction() instanceof PortletAction)
{
PortletAction action = (PortletAction) invocation.getAction();
currIp = action.getRequest().getRemoteAddr();
}
String ip = ApplicationResource.getHotValue("ALLOW_CACHE_IP");
if (StringUtils.isBlank(ip) || StringUtils.isBlank(currIp))
{
log.error("允許重新整理的IP不存在或當前請求的IP非法.");
throw new NoAllowIPException();
}
else
{
String[] ips = ip.split(",");
boolean errorIp = true;
for (String s : ips)
{
if (s.equals(currIp))
errorIp = false;
}
// 判斷IP
if (errorIp)
throw new NoAllowIPException();
}
result = invocation.invoke();//呼叫被攔截的方法
}
catch (Exception e)
{
log.error("異常類名:" + invocation.getAction().getClass());
log.error("異常方法:" + methodName, e);
throw e;
}
return result;
}
}
示例2
作者:知乎使用者
連結:https://www.zhihu.com/question/35225845/answer/61876681
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
class River {
// 流量
int volume;
// 總魚數
int numFish;
}
class PowerGenerator {
double generate (int volume) {
// 假設每一百立方米水發一度電
return volume / 100;
}
}
interface Interceptor {
void intercept (River river);
}
class SomeInterceptor implements Interceptor {
PowerGenerator generator = new PowerGenerator();
@Override
public void intercept (River river) {
// 消耗了1000立方米水來發電
int waterUsed = 1000;
// 水量減少了1000。請不要跟我討論水電站原理的問題,
// 我就那麼一比方
river.volume -= waterUsed;
// 發電
generator.generate (waterUsed);
// 攔截所有的魚
river.numFish = 0;
}
}
class RiverController {
Interceptor interceptor;
void flow(River river) {
// 源頭積累下來的水量和魚
river.volume += 100000
river.numFish += 1000
// 經過了大壩
interceptor.intercept(river);
// 下了點雨
river.volume += 1000
}
void setInterceptor (Interceptor interceptor) {
this.interceptor = interceptor
}
}
class Main {
public static void main (String args[]) {
RiverController rc = new RiverController;
Interceptor inter = new SomeInterceptor();
// 這一步通常叫做控制反轉或依賴注入,其實也沒啥子
rc.setInterceptor(inter);
rc.flow(new River());
}
}
攔截器與過濾器的區別 :
1. 攔截器是基於java的反射機制的,而過濾器是基於函式回撥。 2. 攔截器不依賴與servlet容器,過濾器依賴與servlet容器。 3. 攔截器只能對action請求起作用,而過濾器則可以對幾乎所有的請求起作用。 4. 攔截器可以訪問action上下文、值棧裡的物件,而過濾器不能訪問。 5. 在action的生命週期中,攔截器可以多次被呼叫,而過濾器只能在容器初始化時被呼叫一次攔截器的代 碼實現。 6. Filter基於回撥函式,我們需要實現的filter介面中doFilter方法就是回撥函式,而interceptor則基於 java本身的反射機制,這是兩者最本質的區別。 7. Filter是依賴於servlet容器的,即只能在servlet容器中執行,很顯然沒有servlet容器就無法來回調 doFilter方法。而interceptor與servlet容器無關。 8. Filter的過濾範圍比Interceptor大,Filter除了過濾請求外通過萬用字元可以保護頁面,圖片,檔案等等, 而Interceptor只能過濾請求。 9. Filter的過濾例外一般是在載入的時候在init方法宣告,而Interceptor可以通過在xml宣告是guest請求還 是user請求來辨別是否過濾。
三、監聽器
1.監聽器(Listener):當一個事件發生的時候,你希望獲得這個事件發生的詳細資訊,而並不想幹預這個事件本身的程序,這就要用到監聽器。
2.示例程式碼
作者:知乎使用者
連結:https://www.zhihu.com/question/35225845/answer/61876681
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
// 監聽器
interface BedListener {
// 監聽器在引數中收到了某個事件,而這個事件往往是隻讀的
// 監聽器的方法名通常以"on"開頭
void onBedSound (String sound);
}
class Neighbor {
BedListener listener;
// 依然是所謂控制反轉
setListener (BedListener listener) {
this.listener = listener;
}
void doInterestingStuff () {
// 根據當地法律法規,部分內容無法顯示
// 將事件傳送給監聽器
listener.onBedSound("嘿咻");
listener.onBedSound("oyeah");
}
}
class Main {
public static void main (String args[]) {
Neighbor n = new Neighbor();
n.setListener (sound -> generatePower());
n.doInterestingStuff();
}
private static void generatePower() {
// 根據當地法律法規,部分內容無法顯示
}
}