首先說明一下二者的區別:
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 可以看到控制台打印內容如下:

