Spring MVC 攔截器(Interceptor)與過濾器(Filter)

目錄

一、用戶的普通 Http 請求執行順序

二、過濾器、攔截器添加後的執行順序

三、攔截器(Interceptor)的基本定義

  攔截器是面向切面(AOP)編程中應用的一種統一處理方案,就是在你的 Controller、Servie 或者一個 Method 調用一個 Method,或者在 Method 調用一個 Method 之後,統一的進行處理的方案,基於 Java 的反射機制。

  攔截器,在 AOP(Aspect-Oriented Programming)中可以用於在某個方法或者字段被訪問之前,進行攔截,然後在之前或者之後加入某些統一的處理方法。攔截是 AOP 的一種具象的實現方式。

  攔截器將很多 service 或者 Controller 中共有的行爲提煉出來,在某些方法執行的前後執行,提煉爲通用的處理方式,讓被攔截的方法都能享受這一共有的功能,讓代碼更加簡潔,同時,當共有的功能需要發生調整、變動的時候,不必修改很多的類或者方法,只要修改這個攔截器就可以了,可複用性很強。

  Spring MVC 中的 Interceptor 攔截請求是通過 HandlerInterceptor 來實現的。

四、攔截器(Interceptor)必須實現的三個方法

  1)總覽

  2)preHandle(HttpServletRequest request, HttpServletResponse response, Object handle)方法

    該方法將在請求處理之前進行調用。SpringMVC 中的 Interceptor 是鏈式的調用的,在一個應用中或者說是在一個請求中可以同時存在多個 Interceptor 。每個 Interceptor 的調用會依據它的聲明順序依次執行,而且最先執行的都是 Interceptor 中的 preHandle 方法,所以可以在這個方法中進行一些前置初始化操作或者是對當前請求的一個預處理,也可以在這個方法中進行一些判斷來決定請求是否要繼續進行下去。該方法的返回值是布爾值 Boolean 類型的,當它返回爲 false 時,表示請求結束,後續的 Interceptor 和 Controller 都不會再執行;當返回值爲 true 時就會繼續調用下一個 Interceptor 的 preHandle 方法,如果已經是最後一個 Interceptor 的時候就會是調用當前請求的 Controller 方法。

  3)  postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法  【前提:在當前所屬的 Interceptor 的 preHandle 方法的返回值爲 true 時才能被調用】

    在當前請求進行處理之後,也就是 Controller 方法調用之後執行,但是它會在 DispatcherServlet 進行視圖返回渲染之前被調用,所以我們可以在這個方法中對 Controller 處理之後的 ModelAndView 對象進行操作。postHandle 方法被調用的方向跟 preHandle 是相反的,也就是說先聲明的 Interceptor 的 postHandle 方法反而會後執行,這和 Struts2 裏面的 Interceptor 的執行過程有點類型。Struts2 裏面的 Interceptor 的執行過程也是鏈式的,只是在 Struts2 裏面需要手動調用 ActionInvocation 的 invoke 方法來觸發對下一個 Interceptor 或者是 Action 的調用,然後每一個 Interceptor 中在 invoke 方法調用之前的內容都是按照聲明順序執行的,而 invoke 方法之後的內容就是反向的。  

  4)afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)方法  【前提:在當前所屬的 Interceptor 的 preHandle 方法的返回值爲 true 時才能被調用】

    該方法將在整個請求結束之後,也就是在 DispatcherServlet 渲染了對應的視圖之後執行。這個方法的主要作用是用於進行資源清理工作的。  

五、單個攔截器(Interceptor)的 Demo 實現

  1)初始化攔截器

@Component
public class UserAccessInterceptor implements HandlerInterceptor {

    private final Logger logger = LoggerFactory.getLogger(UserAccessInterceptor.class);

    @Resource
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        //1.獲取headers裏的author-Cookie
     //2.根據cookie使用userService查找當前user
     //3.存在且激活 當前用戶信息設置到ThreadLocal return true;
     //4.不存在 或 未激活 return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
    
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        //銷燬ThreadLocal的用戶信息
    }

  2)攔截器配置類

@Configuration
public class WebInterceptorConfig extends WebMvcConfigurationSupport {

    @Autowired
    private UserAccessInterceptor userAccessInterceptor;

  //不攔截的URL集合
    private static List<String> exclusionUrlList=new ArrayList<>();

    static {
        exclusionUrlList.add("/favicon.ico");
        exclusionUrlList.add("/**/*.css");
        exclusionUrlList.add("/**/*.js");
        exclusionUrlList.add("/ok");
        exclusionUrlList.add("/");
        exclusionUrlList.add("/console/**");
        exclusionUrlList.add("/index");
    }

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this.userAccessInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns(exclusionUrlList);
        super.addInterceptors(registry);
    }

}

六、攔截器(Interceptor)的兩種配置方式

  1)同上 -> Demo 實現

  2)Spring MVC 使用 mvc:interceptors 標籤

<mvc:interceptors>  
<!-- 使用bean定義一個Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截所有的請求 -->  
<bean/>  
    <mvc:interceptor>  
         <mvc:mapping path="/**"/>  
         <mvc:exclude-mapping path="/parent/**"/>  
         <bean />  
    </mvc:interceptor>  
</mvc:interceptors>

七、過濾器(Filter)的基本定義

  Filter 可以認爲是 Servlet 的一種 “加強版”,它主要用於對用戶請求進行預處理,也可以對 HttpServletResponse 進行後處理,是個典型的處理鏈。Filter 也可以對用戶請求生成響應,這一點與 Servlet 相同,但實際上很少會使用 Filter 向用戶請求生成響應。使用 Filter 完整的流程是:Filter 對用戶請求進行預處理,接着將請求交給 Servlet 進行處理並生成響應,最後 Filter 再對服務器響應進行後處理。

  在 Web 中稱之爲 Filter,通過配置多個過濾器,Web 系統可以對所有的 Servlet 請求進行一層一層的過濾,以完成一些特殊的功能。例如常用的資源訪問權限控制、特殊字符以及敏感詞過濾、響應信息壓縮等功能。

  Servlet 中的過濾器 Filter 是實現了 javax.servlet.Filter 接口的服務器端程序,主要的用途是設置字符集、控制權限、控制轉向、做一些業務邏輯判斷等。其工作原理是,只要你在 web.xml 文件配置好要攔截的客戶端請求,它都會幫你攔截到請求,此時你就可以對請求或響應 (Request、Response) 統一設置編碼,簡化操作;同時還可進行邏輯判斷,如用戶是否已經登陸、有沒有權限訪問該頁面等等工作。它是隨你的 web 應用啓動而啓動的,只初始化一次,以後就可以攔截相關請求,只有當你的 web 應用停止或重新部署的時候才銷燬。

八、過濾器(Filter)必須實現的三個方法

  1)總覽

  2)default void init(FilterConfig filterConfig) throws ServletException {}

    用於完成 Filter 的初始化。    

  3)void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;

    實現過濾功能,該方法就是對每個請求及響應增加的額外處理。該方法可以實現對用戶請求進行預處理 (ServletRequest request),也可實現對服務器響應進行後處理 (ServletResponse response)—它們的分界線爲是否調用了 chain.doFilter(), 執行該方法之前,即對用戶請求進行預處理;執行該方法之後,即對服務器響應進行後處理。

  4)  default void destroy() {}

    用於 Filter 銷燬前,完成某些資源的回收。

九、單個過濾器(Filter)的 Demo 實現

  1)TestFilter 初始化

public class TestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
 
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("我是Filter,執行filterChain.doFilter(request,response)之前。");
        filterChain.doFilter(request,response);
        System.out.println("我是Filter,執行filterChain.doFilter(request,response)之後。");
    }
 
    @Override
    public void destroy() {
 
    }

  2)Filter 配置類

@Slf4j
@Configuration
public class TestFilterConfiguration {//不攔截路徑
    private static List<String> exclusionUrlList=new ArrayList<>();
    //攔截路徑
    private static List<String> inclusionUrlList=new ArrayList<>();

    static {
        exclusionUrlList.add("/favicon.ico");
        exclusionUrlList.add("/**/*.css");
        exclusionUrlList.add("/**/*.js");
        exclusionUrlList.add("/ok");
        inclusionUrlList.add("/api/**");
    }

    @Bean
    public FilterRegistrationBean filterRegistration() {
        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new TestFilter());
        registration.addInitParameter(FILTER_INIT_PARAM_EXCLUSION_URLS,String.join(",", exclusionUrlList));
        registration.addInitParameter(AJAX_URL_PATTERNS,String.join(",", inclusionUrlList));
        registration.addUrlPatterns("/*");
        registration.setName("testFilter");
     return registration;
    }

}

十、過濾器(Filter)的三種配置方式

  1)通過 @WebFilter 註解配置

1.初始化Filter

@WebFilter(urlPatterns = "/test001")
@Order(1) //order值越小,過濾器越靠前,此處配置無效
public class TestFilter implements Filter {
    @Override
    public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
        System.out.println("##############TestFilter init##############");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //在DispatcherServlet之前執行
        System.out.println("##############doFilter before##############");
        filterChain.doFilter(servletRequest, servletResponse);
        // 在視圖頁面返回給客戶端之前執行,但是執行順序在Interceptor之後
        System.out.println("##############doFilter after##############");
    }

    @Override
    public void destroy() {
        System.out.println("##############TestFilter destroy##############");
    }
}

//2.在啓動類添加 @ServletComponentScan

@SpringBootApplication 
@ServletComponentScan 
public class TestbootApplication { 
  public static void main(String[] args) { 
    SpringApplication.run(TestbootApplication.class, args); 
  } 
}

  2)通過 @Bean 來配置

//1.初始化Filter

@Component
public class TestFilter3 implements Filter{
    @Override
    public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
        System.out.println("##############Filter3 init##############");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //在DispatcherServlet之前執行
        System.out.println("##############doFilter3 before##############");
        filterChain.doFilter(servletRequest, servletResponse);
        // 在視圖頁面返回給客戶端之前執行,但是執行順序在Interceptor之後
        System.out.println("##############doFilter3 after##############");
    }

    @Override
    public void destroy() {
        System.out.println("##############Filter3 destroy##############");
    }
}
//2.註冊到config
@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean testFilter3RegistrationBean() {
        FilterRegistrationBean registration = new FilterRegistrationBean(new TestFilter3());
        registration.addUrlPatterns("/hello");
        registration.setOrder(1); // 值越小越靠前,此處配置有效
        return registration;
    }

    @Bean
    public FilterRegistrationBean testFilter4RegistrationBean() {
        FilterRegistrationBean registration = new FilterRegistrationBean(new TestFilter4());
        registration.addUrlPatterns("/hello");
        registration.setOrder(2);
        return registration;
    }
}

  3)Spring MVC 在 web.xml 進行配置

    1. 初始化 Filter

    2.web.xml 文件中配置 Filter

<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

十一、攔截器和過濾器的區別

十二、攔截器和過濾器的作用 / 用途

  過濾器用途:用於設置字符編碼、URL 級別的權限控制,敏感詞彙的過濾

  攔截器用途:攔截未登錄的用戶,攔截器和過濾器的功能相近

十三、總結

  1. 過濾器:所謂過濾器顧名思義是用來過濾的,在 java web 中,你傳入的 request,response 提前過濾掉一些信息,或者提前設置一些參數,然後再傳入 servlet 或者 struts 的 action 進行業務邏輯,比如過濾掉非法 url(不是 login.do 的地址請求,如果用戶沒有登陸都過濾掉), 或者在傳入 servlet 或者 struts 的 action 前統一設置字符集,或者去除掉一些非法字符(聊天室經常用到的,一些罵人的話)。filter 流程是線性的, url 傳來之後,檢查之後,可保持原來的流程繼續向下執行,被下一個 filter, servlet 接收等.

  2.java 的攔截器 主要是用在插件上,擴展件上比如 hibernate spring struts2 等 有點類似面向切片的技術,在用之前先要在配置文件即 xml 文件裏聲明一段的那個東西。

十四、參考文獻

  1. https://www.cnblogs.com/panxuejun/p/7715917.html《過濾器(Filter)和攔截器(Interceptor)的執行順序和區別》
  2. https://www.cnblogs.com/junzi2099/p/8022058.html《過濾器(Filter)與攔截器(Interceptor ) 區別》
  3. https://blog.csdn.net/qq_34908167/article/details/80624507《過濾器和攔截器的區別和執行順序》
  4. https://www.cnblogs.com/feibazhf/p/11265145.html《過濾器、攔截器和監聽器區別》
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://www.cnblogs.com/l3306/p/14779295.html