1. 程式人生 > >SpringMVC的攔截器(Interceptor)和過濾器(Filter)的區別與聯系

SpringMVC的攔截器(Interceptor)和過濾器(Filter)的區別與聯系

get err 實例 分享 切面 簡介 () lee XML

一 簡介

(1)過濾器:

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

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

  • 繼承HttpServletRequestWrapper以實現在Filter中修改HttpServletRequest的參數:https://www.zifangsky.cn/677.html

  • 在SpringMVC中使用過濾器(Filter)過濾容易引發XSS的危險字符:https://www.zifangsky.cn/683.html

(2)攔截器:

依賴於web框架,在SpringMVC中就是依賴於SpringMVC框架。在實現上基於Java的反射機制,屬於面向切面編程(AOP)的一種運用。由於攔截器是基於web框架的調用,因此可以使用spring的依賴註入(DI)進行一些業務操作,同時一個攔截器實例在一個controller生命周期之內可以多次調用。但是缺點是只能對controller請求進行攔截,對其他的一些比如直接訪問靜態資源的請求則沒辦法進行攔截處理

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

  • 在SpringMVC中使用攔截器(interceptor)攔截CSRF攻擊(修):https://www.zifangsky.cn/671.html

  • SpringMVC中使用Interceptor+cookie實現在一定天數之內自動登錄:https://www.zifangsky.cn/700.html

二 多個過濾器與攔截器的代碼執行順序

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

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

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

i)過濾器1:

[java] view plain copy
  1. <a target="_blank" href="http://www.07net01.com/tags-package-0.html" class="infotextkey" style="background:transparent; color:rgb(66,139,202)">package</a> cn.zifangsky.filter;
  2. import java.io.IOException;
  3. import javax.servlet.FilterChain;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import org.springframework.web.filter.OncePerRequestFilter;
  8. public class TestFilter1 extends OncePerRequestFilter {
  9. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  10. throws ServletException, IOException {
  11. //在DispatcherServlet之前執行
  12. <a target="_blank" href="http://www.07net01.com/tags-system-0.html" class="infotextkey" style="background:transparent; color:rgb(66,139,202)">system</a>.out.println("############TestFilter1 doFilterInternal executed############");
  13. filterChain.doFilter(request, response);
  14. //在視圖頁面返回給<a target="_blank" href="http://www.07net01.com/tags-%E5%AE%A2%E6%88%B7%E7%AB%AF-0.html" class="infotextkey" style="background:transparent; color:rgb(66,139,202)">客戶端</a>之前執行,但是執行順序在Interceptor之後
  15. System.out.println("############TestFilter1 doFilter after############");
  16. // try {
  17. // Thread.sleep(10000);
  18. // } catch (InterruptedException e) {
  19. // e.printStackTrace();
  20. // }
  21. }
  22. }

ii)過濾器2:

[java] view plain copy
  1. package cn.zifangsky.filter;
  2. import java.io.IOException;
  3. import javax.servlet.FilterChain;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import org.springframework.web.filter.OncePerRequestFilter;
  8. public class TestFilter2 extends OncePerRequestFilter {
  9. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  10. throws ServletException, IOException {
  11. System.out.println("############TestFilter2 doFilterInternal executed############");
  12. filterChain.doFilter(request, response);
  13. System.out.println("############TestFilter2 doFilter after############");
  14. }
  15. }

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

[xml] view plain copy
  1. <!-- 自定義過濾器:testFilter1 -->
  2. <filter>
  3. <filter-name>testFilter1</filter-name>
  4. <filter-class>cn.zifangsky.filter.TestFilter1</filter-class>
  5. </filter>
  6. <filter-mapping>
  7. <filter-name>testFilter1</filter-name>
  8. <url-pattern>/*</url-pattern>
  9. </filter-mapping>
  10. <!-- 自定義過濾器:testFilter2 -->
  11. <filter>
  12. <filter-name>testFilter2</filter-name>
  13. <filter-class>cn.zifangsky.filter.TestFilter2</filter-class>
  14. </filter>
  15. <filter-mapping>
  16. <filter-name>testFilter2</filter-name>
  17. <url-pattern>/*</url-pattern>
  18. </filter-mapping>

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

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

[java] view plain copy
  1. package cn.zifangsky.interceptor;
  2. import javax.servlet.http.HttpServletRequest;
  3. import javax.servlet.http.HttpServletResponse;
  4. import org.springframework.web.servlet.HandlerInterceptor;
  5. import org.springframework.web.servlet.ModelAndView;
  6. public class BaseInterceptor implements HandlerInterceptor{
  7. /**
  8. * 在DispatcherServlet之前執行
  9. * */
  10. public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
  11. System.out.println("************BaseInterceptor preHandle executed**********");
  12. return true;
  13. }
  14. /**
  15. * 在controller執行之後的DispatcherServlet之後執行
  16. * */
  17. public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
  18. throws Exception {
  19. System.out.println("************BaseInterceptor postHandle executed**********");
  20. }
  21. /**
  22. * 在頁面渲染完成返回給客戶端之前執行
  23. * */
  24. public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
  25. throws Exception {
  26. System.out.println("************BaseInterceptor afterCompletion executed**********");
  27. // Thread.sleep(10000);
  28. }
  29. }

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

[java] view plain copy
  1. package cn.zifangsky.interceptor;
  2. import javax.servlet.http.HttpServletRequest;
  3. import javax.servlet.http.HttpServletResponse;
  4. import org.springframework.web.servlet.HandlerInterceptor;
  5. import org.springframework.web.servlet.ModelAndView;
  6. public class TestInterceptor implements HandlerInterceptor {
  7. public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
  8. System.out.println("************TestInterceptor preHandle executed**********");
  9. return true;
  10. }
  11. public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
  12. throws Exception {
  13. System.out.println("************TestInterceptor postHandle executed**********");
  14. }
  15. public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
  16. throws Exception {
  17. System.out.println("************TestInterceptor afterCompletion executed**********");
  18. }
  19. }

iii)在SpringMVC的配置文件中註冊這兩個攔截器:

[java] view plain copy
  1. <!-- 攔截器 -->
  2. nbsp; <mvc:interceptors>
  3. <!-- 對所有請求都攔截,公共攔截器可以有多個 -->
  4. <bean name="baseInterceptor" class="cn.zifangsky.interceptor.BaseInterceptor" />
  5. <!-- <bean name="testInterceptor" class="cn.zifangsky.interceptor.TestInterceptor" /> -->
  6. <mvc:interceptor>
  7. <!-- 對/test.html進行攔截 -->
  8. <mvc:mapping path="/test.html"/>
  9. <!-- 特定請求的攔截器只能有一個 -->
  10. <bean class="cn.zifangsky.interceptor.TestInterceptor" />
  11. </mvc:interceptor>
  12. </mvc:interceptors>

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

[java] view plain copy
  1. package cn.zifangsky.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.servlet.ModelAndView;
  5. @Controller
  6. public class TestController {
  7. @RequestMapping("/test.html")
  8. public ModelAndView handleRequest(){
  9. System.out.println("---------TestController executed--------");
  10. return new ModelAndView("test");
  11. }
  12. }

(4)視圖頁面test.jsp:

[html] view plain copy
  1. <%@ page language="java" contentType="text/html; charset=UTF-8"
  2. pageEncoding="UTF-8"%>
  3. <%
  4. String path = request.getContextPath();
  5. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
  6. %>
  7. <html>
  8. <head>
  9. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  10. <base href="http://983836259.blog.51cto.com/7311475/">
  11. <title>FilterDemo</title>
  12. </head>
  13. <body>
  14. <%
  15. System.out.println("test.jsp is loading");
  16. %>
  17. <div align="center">
  18. This is test page
  19. </div>
  20. </body>
  21. </html>

(5)測試效果:

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

技術分享

這就說明了過濾器的運行是依賴於servlet容器的,跟springmvc等框架並沒有關系。並且,多個過濾器的執行順序跟xml文件中定義的先後關系有關

接著清空控制臺中的輸出內容並訪問:http://localhost:9180/FilterDemo/test.html

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

技術分享

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

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

技術分享

SpringMVC的攔截器(Interceptor)和過濾器(Filter)的區別與聯系