天天看點

基于SpringMVC的攔截器(Interceptor)和過濾器(Filter)的差別與聯系一 簡介二 多個過濾器與攔截器的代碼執行順序

(1)過濾器:

依賴于servlet容器。在實作上基于函數回調,可以對幾乎所有請求進行過濾,但是缺點是一個過濾器執行個體隻能在容器初始化時調用一次。使用過濾器的目的是用來做一些過濾操作,擷取我們想要擷取的資料,比如:在過濾器中修改字元編碼;在過濾器中修改HttpServletRequest的一些參數,包括:過濾低俗文字、危險字元等

關于過濾器的一些用法可以參考我寫過的這些文章:

(2)攔截器:

依賴于web架構,在SpringMVC中就是依賴于SpringMVC架構。在實作上基于Java的反射機制,屬于面向切面程式設計(AOP)的一種運用。由于攔截器是基于web架構的調用,是以可以使用Spring的依賴注入(DI)進行一些業務操作,同時一個攔截器執行個體在一個controller生命周期之内可以多次調用。但是缺點是隻能對controller請求進行攔截,對其他的一些比如直接通路靜态資源的請求則沒辦法進行攔截處理

如果在一個項目中僅僅隻有一個攔截器或者過濾器,那麼我相信相對來說了解起來是比較容易的。但是我們是否思考過:如果一個項目中有多個攔截器或者過濾器,那麼它們的執行順序應該是什麼樣的?或者再複雜點,一個項目中既有多個攔截器,又有多個過濾器,這時它們的執行順序又是什麼樣的呢?

下面我将用簡單的代碼來測試說明:

(1)先定義兩個過濾器:

i)過濾器1:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

<code>package</code> <code>cn.zifangsky.filter;</code>

<code>import</code> <code>java.io.IOException;</code>

<code>import</code> <code>javax.servlet.FilterChain;</code>

<code>import</code> <code>javax.servlet.ServletException;</code>

<code>import</code> <code>javax.servlet.http.HttpServletRequest;</code>

<code>import</code> <code>javax.servlet.http.HttpServletResponse;</code>

<code>import</code> <code>org.springframework.web.filter.OncePerRequestFilter;</code>

<code>public</code> <code>class</code> <code>TestFilter1 </code><code>extends</code> <code>OncePerRequestFilter {</code>

<code>    </code><code>protected</code> <code>void</code> <code>doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)</code>

<code>            </code><code>throws</code> <code>ServletException, IOException {</code>

<code>        </code><code>//在DispatcherServlet之前執行</code>

<code>        </code><code>System.out.println(</code><code>"############TestFilter1 doFilterInternal executed############"</code><code>);</code>

<code>        </code><code>filterChain.doFilter(request, response);</code>

<code>        </code><code>//在視圖頁面傳回給用戶端之前執行,但是執行順序在Interceptor之後</code>

<code>        </code><code>System.out.println(</code><code>"############TestFilter1 doFilter after############"</code><code>);</code>

<code>//      try {</code>

<code>//          Thread.sleep(10000);</code>

<code>//      } catch (InterruptedException e) {</code>

<code>//          e.printStackTrace();</code>

<code>//      }</code>

<code>    </code><code>}</code>

<code>}</code>

ii)過濾器2:

<code>public</code> <code>class</code> <code>TestFilter2 </code><code>extends</code> <code>OncePerRequestFilter {</code>

<code>        </code><code>System.out.println(</code><code>"############TestFilter2 doFilterInternal executed############"</code><code>);</code>

<code>        </code><code>System.out.println(</code><code>"############TestFilter2 doFilter after############"</code><code>);</code>

iii)在web.xml中注冊這兩個過濾器:

<code>    </code><code>&lt;!-- 自定義過濾器:testFilter1 --&gt;</code>

<code>   </code><code>&lt;</code><code>filter</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>filter-name</code><code>&gt;testFilter1&lt;/</code><code>filter-name</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>filter-class</code><code>&gt;cn.zifangsky.filter.TestFilter1&lt;/</code><code>filter-class</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>filter</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>filter-mapping</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>url-pattern</code><code>&gt;/*&lt;/</code><code>url-pattern</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>filter-mapping</code><code>&gt;</code>

<code>    </code><code>&lt;!-- 自定義過濾器:testFilter2 --&gt;</code>

<code>        </code><code>&lt;</code><code>filter-name</code><code>&gt;testFilter2&lt;/</code><code>filter-name</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>filter-class</code><code>&gt;cn.zifangsky.filter.TestFilter2&lt;/</code><code>filter-class</code><code>&gt;</code>

(2)再定義兩個攔截器:

i)攔截器1,基本攔截器:

29

30

31

32

33

34

35

36

<code>package</code> <code>cn.zifangsky.interceptor;</code>

<code>import</code> <code>org.springframework.web.servlet.HandlerInterceptor;</code>

<code>import</code> <code>org.springframework.web.servlet.ModelAndView;</code>

<code>public</code> <code>class</code> <code>BaseInterceptor </code><code>implements</code> <code>HandlerInterceptor{</code>

<code>    </code> 

<code>    </code><code>/**</code>

<code>     </code><code>* 在DispatcherServlet之前執行</code>

<code>     </code><code>* */</code>

<code>    </code><code>public</code> <code>boolean</code> <code>preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) </code><code>throws</code> <code>Exception {</code>

<code>        </code><code>System.out.println(</code><code>"************BaseInterceptor preHandle executed**********"</code><code>);</code>

<code>        </code><code>return</code> <code>true</code><code>;</code>

<code>     </code><code>* 在controller執行之後的DispatcherServlet之後執行</code>

<code>    </code><code>public</code> <code>void</code> <code>postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)</code>

<code>            </code><code>throws</code> <code>Exception {</code>

<code>        </code><code>System.out.println(</code><code>"************BaseInterceptor postHandle executed**********"</code><code>);</code>

<code>     </code><code>* 在頁面渲染完成傳回給用戶端之前執行</code>

<code>    </code><code>public</code> <code>void</code> <code>afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)</code>

<code>        </code><code>System.out.println(</code><code>"************BaseInterceptor afterCompletion executed**********"</code><code>);</code>

<code>//      Thread.sleep(10000);</code>

ii)指定controller請求的攔截器:

<code>public</code> <code>class</code> <code>TestInterceptor </code><code>implements</code> <code>HandlerInterceptor {</code>

<code>        </code><code>System.out.println(</code><code>"************TestInterceptor preHandle executed**********"</code><code>);</code>

<code>        </code><code>System.out.println(</code><code>"************TestInterceptor postHandle executed**********"</code><code>);</code>

<code>        </code><code>System.out.println(</code><code>"************TestInterceptor afterCompletion executed**********"</code><code>);</code>

iii)在SpringMVC的配置檔案中注冊這兩個攔截器:

<code>    </code><code>&lt;!-- 攔截器 --&gt;</code>

<code>   </code><code>&lt;mvc:interceptors&gt;</code>

<code>        </code><code>&lt;!-- 對所有請求都攔截,公共攔截器可以有多個 --&gt;</code>

<code>        </code><code>&lt;bean name=</code><code>"baseInterceptor"</code> <code>class</code><code>=</code><code>"cn.zifangsky.interceptor.BaseInterceptor"</code> <code>/&gt;</code>

<code>        </code><code>&lt;!-- &lt;bean name=</code><code>"testInterceptor"</code> <code>class</code><code>=</code><code>"cn.zifangsky.interceptor.TestInterceptor"</code> <code>/&gt; --&gt;</code>

<code>        </code><code>&lt;mvc:interceptor&gt;    </code>

<code>            </code><code>&lt;!-- 對/test.html進行攔截 --&gt;</code>

<code>            </code><code>&lt;mvc:mapping path=</code><code>"/test.html"</code><code>/&gt;</code>

<code>            </code><code>&lt;!-- 特定請求的攔截器隻能有一個 --&gt;</code>

<code>            </code><code>&lt;bean </code><code>class</code><code>=</code><code>"cn.zifangsky.interceptor.TestInterceptor"</code> <code>/&gt;</code>

<code>        </code><code>&lt;/mvc:interceptor&gt;</code>

<code>    </code><code>&lt;/mvc:interceptors&gt;</code>

(3)定義一個測試使用的controller:

<code>package</code> <code>cn.zifangsky.controller;</code>

<code>import</code> <code>org.springframework.stereotype.Controller;</code>

<code>import</code> <code>org.springframework.web.bind.annotation.RequestMapping;</code>

<code>@Controller</code>

<code>public</code> <code>class</code> <code>TestController {</code>

<code>    </code><code>@RequestMapping</code><code>(</code><code>"/test.html"</code><code>)</code>

<code>    </code><code>public</code> <code>ModelAndView handleRequest(){</code>

<code>        </code><code>System.out.println(</code><code>"---------TestController executed--------"</code><code>);</code>

<code>        </code><code>return</code> <code>new</code> <code>ModelAndView(</code><code>"test"</code><code>);</code>

(4)視圖頁面test.jsp:

<code>&lt;%@ page language="java" contentType="text/html; charset=UTF-8"</code>

<code>    </code><code>pageEncoding="UTF-8"%&gt;</code>

<code>&lt;%</code>

<code>String path = request.getContextPath();</code>

<code>String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";</code>

<code>%&gt;    </code>

<code>&lt;</code><code>html</code><code>&gt;</code>

<code>&lt;</code><code>head</code><code>&gt;</code>

<code>&lt;</code><code>meta</code> <code>http-equiv</code><code>=</code><code>"Content-Type"</code> <code>content</code><code>=</code><code>"text/html; charset=UTF-8"</code><code>&gt;</code>

<code>&lt;</code><code>base</code> <code>href="&lt;%=basePath%&gt;"&gt;</code>

<code>&lt;</code><code>title</code><code>&gt;FilterDemo&lt;/</code><code>title</code><code>&gt;</code>

<code>&lt;/</code><code>head</code><code>&gt;</code>

<code>&lt;</code><code>body</code><code>&gt;</code>

<code>    </code><code>&lt;%</code>

<code>        </code><code>System.out.println("test.jsp is loading");</code>

<code>    </code><code>%&gt;</code>

<code>    </code><code>&lt;</code><code>div</code> <code>align</code><code>=</code><code>"center"</code><code>&gt;</code>

<code>        </code><code>This is test page</code>

<code>    </code><code>&lt;/</code><code>div</code><code>&gt;</code>

<code>&lt;/</code><code>body</code><code>&gt;</code>

<code>&lt;/</code><code>html</code><code>&gt;</code>

(5)測試效果:

啟動此測試項目,可以看到控制台中輸出如下:

<a href="http://s3.51cto.com/wyfs02/M01/8B/39/wKioL1hHhYrRCpQYAABafsYR7go378.png" target="_blank"></a>

這就說明了過濾器的運作是依賴于servlet容器的,跟springmvc等架構并沒有關系。并且,多個過濾器的執行順序跟xml檔案中定義的先後關系有關

接着清空控制台中的輸出内容并通路:http://localhost:9180/FilterDemo/test.html

可以看到,此時的控制台輸出結果如下:

<a href="http://s2.51cto.com/wyfs02/M01/8B/3D/wKiom1hHhaPRQuBxAACG4WdOJbM758.png" target="_blank"></a>

相信從這個列印輸出,大家就可以很清晰地看到有多個攔截器和過濾器存在時的整個執行順序了。當然,對于過個攔截器它們之間的執行順序跟在SpringMVC的配置檔案中定義的先後順序有關

注:對于整個SpringMVC的執行流程來說,如果加上上面的攔截器和過濾器,其最終的執行流程就如下圖所示:

<a href="http://s2.51cto.com/wyfs02/M02/8B/3D/wKiom1hHhbmxseDtAACidU9Y84s787.png" target="_blank"></a>

本文轉自 pangfc 51CTO部落格,原文連結:http://blog.51cto.com/983836259/1880286,如需轉載請自行聯系原作者