SpringMVC:攔截器和過濾器


首先說明一下二者的區別:

  1. 攔截器基於java的反射機制,而過濾器是基於函數回調

  2. 攔截器不依賴於servlet容器,過濾器依賴servlet容器

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

  4. 在action的生命周期中,攔截器可以多次被調用,而過濾器只能在容器初始化時被調用一次

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

過濾器和攔截器之間的關系如下圖,Filter包裹Servlet,Servlet包裹Interceptor

 過濾器的觸發時機是容器后,servlet之前,所以過濾器的  doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) 的入參是ServletRequest,而不是HttpServletRequest,因為過濾器在HttpServlet之前

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

    System.out.println("before...");

    chain.doFilter(request, response);

    System.out.println("after...");
}

 這個chain.doFilter(request, response) 作用是將請求轉發給過濾器鏈上下一個對象,下一個對象是指filter,如果沒有filter那就是你請求的資源

 攔截器是被包裹在過濾器之中

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("preHandle");
    return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    System.out.println("postHandle");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    System.out.println("afterCompletion");
}

preHandle 這個方法是在過濾器的 chain.doFilter( request, response ) 方法的前一步執行,且在調用Controller之前調用,當返回false后,會跳過之后的攔截器,並且不會執行所有的攔截器 postHandle ,並且調用返回true的攔截器的 afterCompletion 方法

postHandle 是調用 Controller 之后被調用,但是在渲染 View 頁面之前

afterCompletion 是調用完 Controller 接口,渲染 View 頁面最后調用,返回true的攔截器都會調用該攔截器的 afterCompletion 方法,順序相反。這個方法也是在過濾器返回給前端前一步執行 

 

多個過濾器和攔截器是如何配置的呢,下面結合Spring Boot寫個demo,地址是 https://gitee.com/colin220/intercept

首先寫一個Controller,內容如下:

@Controller
public class InterceptController {

    @RequestMapping("/index")
    public ResponseEntity<?> index() {
        System.out.println("Controller        Handler");
        return ResponseEntity.ok("index");
    }
}

這個Controller比較簡單,瀏覽器訪問 http://localhost:8080/index 會返回一個字符串 "index"。

寫三個過濾器,需要實現javax.servlet.Filter接口,實現其中的三個方法。這三個過濾器分別命名為 FirstFilter SecondFilter ThirdFilter,以FirstFilter為例,其他的兩個類內容和其基本一致,其代碼內容如下:

import javax.servlet.*;
import java.io.IOException;

@component
public class FirstFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("FirstFilter init"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("FirstFilter doFilter pre"); filterChain.doFilter(servletRequest, servletResponse); System.out.println("FirstFilter doFilter aft"); } @Override public void destroy() { System.out.println("FirstFilter destroy"); } }

然后寫一個配置類,來配置這三個過濾器,其代碼內容如下:

@Configuration
public class FilterConfig {
  
  @Autowired
  private FirstFilter firstFilter;
  @Autowired
  private SecondFilter secondFilter;
  @Autowired
  private ThirdFilter thirdFilter; @Bean
public FilterRegistrationBean filterRegistrationBeanFirst() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
// 可以采用 spring 的依賴注入 20191117update filterRegistrationBean.setFilter(firstFilter
); filterRegistrationBean.addUrlPatterns("/index/*"); filterRegistrationBean.setName("FirstFilter"); filterRegistrationBean.setOrder(1); return filterRegistrationBean; } @Bean public FilterRegistrationBean filterRegistrationBeanSecond() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new SecondFilter()); filterRegistrationBean.addUrlPatterns("/index/*"); filterRegistrationBean.setName("SecondFilter"); filterRegistrationBean.setOrder(2); return filterRegistrationBean; } @Bean public FilterRegistrationBean filterRegistrationBeanThird() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new ThirdFilter()); filterRegistrationBean.addUrlPatterns("/index/*"); filterRegistrationBean.setName("ThirdFilter"); filterRegistrationBean.setOrder(3); return filterRegistrationBean; } }

因為有三個Filter,因此需要三個org.springframework.boot.web.servlet.FilterRegistrationBean,每個FilterRegisterationBean中需要設置屬性 filterRegistrationBean.setFilter(new FirstFilter( )) ,並設置這個Filter攔截的url,順序order等屬性。

 

接下來寫三個攔截器,需要實現 org.springframework.web.servlet.HandlerInterceptor 接口,重寫其中的三個方法,這三個攔截器分別命名為FirstInterceptor SecondInterceptor ThirdInterceptor,以FirstInterceptor為例,其他兩個類內容基本一致,其代碼內容如下:

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@component
public class FirstInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 在請求處理之前進行調用(Controller方法調用之前) System.out.println("FirstInterceptor preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 請求處理之后進行調用,但是在視圖被渲染之前(Controller方法調用之后 ) System.out.println("FirstInterceptor postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 在整個請求結束之后被調用,也就是在DispatcherServlet 渲染了對應的視圖之后執行(主要是用於進行資源清理工作) System.out.println("FirstInterceptor afterCompletion"); } }

然后寫一個配置類,來配置這三個攔截器。這個配置類要實現org.springframework.web.servlet.config.annotation.WebMvcConfigurer。因為WebMvcConfigurerAdapter 在Spring5.0已被廢棄,所以采用WebMvcConfigurer,其代碼內容如下:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
   @Autowired
private FirstInterceptor firstInterceptor;
@Autowired
private SecondInterceptor secondInterceptor;
@Autowired
private ThirdInterceptor thirdInterceptor;
     @Override
public void addInterceptors(InterceptorRegistry registry) {
     // 可以將 FirstInterceptor 之類的由 spring 進行管理 不建議采用 new 的方法創建對象 20191117update registry.addInterceptor(firstInterceptor
).addPathPatterns("/index/**"); registry.addInterceptor(secondInterceptor).addPathPatterns("/index/**"); registry.addInterceptor(thirdInterceptor).addPathPatterns("/index/**"); } }

其中InterceptorRegistry內部有個List,這個addInterceptor( )方法是用來存放添加進去的interceptor,按照添加的先后順序,依次攔截。 

這樣就完成了多個過濾器和多個攔截器的配置,瀏覽器訪問 http://localhost:8080/index 可以看到控制台打印內容如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM