1. 程式人生 > >過濾器與攔截器區別、使用場景筆記整理

過濾器與攔截器區別、使用場景筆記整理

①攔截器是基於java的反射機制的,而過濾器是基於函式回撥。
  ②攔截器不依賴與servlet容器,過濾器依賴與servlet容器。
  ③攔截器只能對action請求起作用,而過濾器則可以對幾乎所有的請求起作用。
  ④攔截器可以訪問action上下文、值棧裡的物件,而過濾器不能訪問。
  ⑤在action的生命週期中,攔截器可以多次被呼叫,而過濾器只能在容器初始化時被呼叫一次。

  6.攔截器可以獲取IOC容器中的各個bean,而過濾器就不行,這點很重要,在攔截器裡注入一個service,可以呼叫業務邏輯。

另摘自http://blog.csdn.net/chenleixing/article/details/44573495#reply ,疑似《java程式設計思想》截圖

這本書對java開發者來說確實是奇書,雖然不管什麼時候看都是似懂非懂,而且每次都沒看完。但確實有幫助,推薦1-2年開發者看

 

大家請看我的web.xml過濾器配置,依次 為shiro許可權過濾器,編碼過濾器,微信介面過濾器,上傳檔案過濾器

複製程式碼

複製程式碼

<!-- shiro過濾器定義 -->
  <filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
      <!-- 該值預設為false,表示生命週期由SpringApplicationContext管理,設定為true則表示由ServletContainer管理 -->
      <param-name>targetFilterLifecycle</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <!-- 編碼過濾器 -->
  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!--<async-supported>true</async-supported>-->
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>


  <!--微信介面訪問-->
  <filter>
    <filter-name>WXInterface</filter-name>
    <filter-class>com.shangtu.filter.WXInterfaceFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>WXInterface</filter-name>
    <url-pattern>/WX/*</url-pattern><!-- 你開放的介面字首  -->
  </filter-mapping>

  <!--上傳介面訪問-->
  <filter>
    <filter-name>UploadInterface</filter-name>
    <filter-class>com.shangtu.filter.FileUploadFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>UploadInterface</filter-name>
    <url-pattern>/upload/*</url-pattern><!-- 你開放的介面字首  -->
  </filter-mapping>

複製程式碼

複製程式碼

然後很不幸的我的專案中沒有使用interceptor(第一次自己搭建專案,很多功能需求還在確認中),使用的是aspect,以下以日誌記錄為例:

1、註冊bean,開啟aspect註解支援,開啟aspect註解掃描

 View Code

2、建立自定義註解類,達到靈活註解的目的

 View Code

3、寫入邏輯,日誌為操作成功後記錄

 View Code

至此,只要在controller方法中加入

@SyslogAnnotation(modelName="代理中心", functionName="給代理髮卡", commons="userId:登入ID,agentId:被髮卡人ID,card:髮卡數量")

即可獲取引數,插入自定義日誌到資料庫中 。

而關於Aspect與interceptor的關係,大致百度了一下(不能不信,也不能全信,希望以後能在這方面繼續完善更新) 

AOP是一種將業務邏輯提取出來,以實現程式碼複用,易於維護的方式,攔截器是它的一種具體實現,aspectJ也是一種具體實現,但攔截器具有程式碼侵入性,aspectJ具有相比更低的侵入性,更好,易於維護

根據以上程式碼事實可以確認的是,

1、大量的請求塊資訊處理使用filter,  特別的內部邏輯處理所使用aspect

2、filter是servlet級別的,不屬於spring      aspect和interceptor屬於spring框架 

3、filter配置在web.xml中    interceptor配置在spring配置檔案中 

4、filter的作用範圍中可以包含aspect和interceptor

 

 

攔截器與過濾器的區別總結:

 

 

過濾器在web.xml中配置:

(1)因為一開始在過濾器中對映的url-pattern填寫路徑是*.action。所有的action要經過它的過濾。<url-pattern>*.action</url-pattern>

(2)因為在web.xml中配置了過濾器,所以,執行過程所有程式都經過struts2過濾器(路徑是:/*),首先過濾自己相關的程式,如xx.action,先處理,如果是.html或者.jsp就不會處理,直接放行。

解決方法:把過濾器對映路徑改為:/*  ,即全部過濾處理。

<url-pattern>/*</url-pattern>

過濾器和攔截器不同,struts中攔截器是核心。

 

理解

(1)過濾器(Filter):當你有一堆東西的時候,你只希望選擇符合你要求的某一些東西。定義這些要求的工具,就是過濾器。(理解:就是一堆字母中取一個B)

(2)攔截器(Interceptor):在一個流程正在進行的時候,你希望干預它的進展,甚至終止它進行,這是攔截器做的事情。(理解:就是一堆字母中,干預他,通過驗證的少點,順便乾點別的東西)。
 

攔截器 :是在面向切面程式設計的就是在你的service或者一個方法,前呼叫一個方法,或者在方法後呼叫一個方法比如動態代理就是攔截器的簡單實現,在你呼叫方法前打印出字串(或者做其它業務邏輯的操作),也可以在你呼叫方法後打印出字串,甚至在你丟擲異常的時候做業務邏輯的操作。

過濾器:是在javaweb中,你傳入的request、response提前過濾掉一些資訊,或者提前設定一些引數,然後再傳入servlet或者struts的action進行業務邏輯,比如過濾掉非法url(不是login.do的地址請求,如果使用者沒有登陸都過濾掉),或者在傳入servlet或者 struts的action前統一設定字符集,或者去除掉一些非法字元.。

 

攔截器與過濾器使用場景:

spring Web MVC的處理器攔截器類似於Servlet開發中的過濾器Filter,用於對處理器進行預處理和後處理。

 

1、日誌記錄:記錄請求資訊的日誌,以便進行資訊監控、資訊統計、計算PV(Page View)等。

2、許可權檢查:如登入檢測,進入處理器檢測檢測是否登入,如果沒有直接返回到登入頁面;

3、效能監控:有時候系統在某段時間莫名其妙的慢,可以通過攔截器在進入處理器之前記錄開始時間,在處理完後記錄結束時間,從而得到該請求的處理時間(如果有反向代理,如apache可以自動記錄);

4、通用行為:讀取cookie得到使用者資訊並將使用者物件放入請求,從而方便後續流程使用,還有如提取Locale、Theme資訊等,只要是多個處理器都需要的即可使用攔截器實現。

5、OpenSessionInView:如hibernate,在進入處理器開啟Session,在完成後關閉Session。

 

 

具體區別

 

攔截器是AOP( Aspect-Oriented Programming)的一種實現,底層通過動態代理模式完成。

區別:

(1)攔截器是基於java的反射機制的,而過濾器是基於函式回撥。

(2)攔截器不依賴於servlet容器,而過濾器依賴於servlet容器。

(3)攔截器只能對action請求起作用,而過濾器則可以對幾乎所有的請求起作用。

(4)攔截器可以訪問action上下文、值棧裡的物件,而過濾器不能。

(5)在action的生命週期中,攔截器可以多次被呼叫,而過濾器只能在容器初始化時被呼叫一次。

 

 

 兩者的本質區別:從靈活性上說攔截器功能更強大些,Filter能做的事情,他都能做,而且可以在請求前,請求後執行,比較靈活。Filter主要是針對URL地址做一個編碼的事情、過濾掉沒用的引數、安全校驗(比較泛的,比如登入不登入之類),太細的話,還是建議用interceptor。不過還是根據不同情況選擇合適的。

 

 

 

filter

Interceptor

多個的執行順序

根據filter mapping配置的先後順序

按照配置的順序,但是可以通過order控制順序

規範

在Servlet規範中定義的,是Servlet容器支援的

spring容器內的,是Spring框架支援的。

使用範圍

只能用於Web程式中

既可以用於Web程式,也可以用於Application、Swing程式中。

深度

Filter在只在Servlet前後起作用

攔截器能夠深入到方法前後、異常丟擲前後等

 

 

 
補充說明:
    Spring的攔截器與Servlet的Filter有相似之處,比如二者都是AOP程式設計思想的體現,都能實現許可權檢查、日誌記錄等。不同的是:

     使用範圍不同:Filter是Servlet規範規定的,只能用於Web程式中。而攔截器既可以用於Web程式,也可以用於Application、Swing程式中。
     規範不同:Filter是在Servlet規範中定義的,是Servlet容器支援的。而攔截器是在Spring容器內的,是Spring框架支援的。

使用的資源不同:同其他的程式碼塊一樣,攔截器也是一個Spring的元件,歸Spring管理,配置在Spring檔案中,因此能使用Spring裡的任何資源、物件,例如Service物件、資料來源、事務管理等,通過IoC注入到攔截器即可;而Filter則不能。

深度不同:Filter在只在Servlet前後起作用。而攔截器能夠深入到方法前後、異常丟擲前後等,因此攔截器的使用具有更大的彈性。所以在Spring構架的程式中,要優先使用攔截器。

  實際上Filter和Servlet極其相似,區別只是Filter不能直接對使用者生成響應。實際上Filter裡doFilter()方法裡的程式碼就是從多個Servlet的service()方法裡抽取的通用程式碼,通過使用Filter可以實現更好的複用。
  filter是一個可以複用的程式碼片段,可以用來轉換HTTP請求、響應和頭資訊。Filter不像Servlet,它不能產生一個請求或者響 應,它只是修改對某一資源的請求,或者修改從某一的響應。
  JSR中說明的是,按照多個匹配的Filter,是按照其在web.xml中配置的順序 來執行的。 

所以這也就是,把自己的Filter或者其他的Filter(比如UrlRewrite的Filter)放在Struts的 DispatcherFilter的前面的原因。因為,它們需要在請求被Struts2框架處理之前,做一些前置的工作。 

        當Filter被呼叫,並且進入了Struts2的DispatcherFilter中 後,Struts2會按照在Action中配置的Interceptor Stack中的Interceptor的順序,來呼叫Interceptor。

 

 

 

 

 

一、過濾器 
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());



二、攔截器 
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() {
    // 根據當地法律法規,部分內容無法顯示
  }
}
--------------------- 
作者:suk1M 
來源:CSDN 
原文:https://blog.csdn.net/w214019153/article/details/68491523 
版權宣告:本文為博主原創文章,轉載請附上博文連結!