原文博客地址: pjmike的博客java
以前實際開發項目的時候,雖然有用過濾器和攔截器,可是理解上仍是有點懵懵懂懂的,沒有完全明白,這篇文章就來仔細剖析下這兩者的區別與聯繫。git
過濾器Filter,是在Servlet規範中定義的,是Servlet容器支持的,該接口定義在 javax.servlet
包下,主要是在客戶端請求(HttpServletRequest)進行預處理,以及對服務器響應(HttpServletResponse)進行後處理。接口代碼以下:github
package javax.servlet;
import java.io.IOException;
public interface Filter {
void init(FilterConfig var1) throws ServletException;
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
void destroy();
}
複製代碼
對上面三個接口方法進行分析:web
FilterChain.doFilter
能夠將請求繼續傳遞下去,若是想攔截這個請求,能夠不調用FilterChain.doFilter,那麼這個請求就直接返回了,因此Filter是一種責任鏈設計模式,在spring security
就大量使用了過濾器,有一條過濾器鏈。在springboot自定義Filter類以下:spring
@Component
public class MyFilter implements Filter {
private Logger logger = LoggerFactory.getLogger(MyFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
logger.info("filter init");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.info("doFilter");
//對request,response進行預處理
//TODO 進行業務邏輯
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
logger.info("filter destroy");
}
}
複製代碼
在springboot中提供了FilterRegistrationBean
方式,此類提供setOrder方法,能夠爲多個filter設置排序值。代碼以下:設計模式
@Configuration
public class FilterConfig {
/** * 配置一個Filter註冊器 * * @return */
@Bean
public FilterRegistrationBean filterRegistrationBean1() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter1());
registrationBean.setName("filter1");
//設置順序
registrationBean.setOrder(10);
return registrationBean;
}
@Bean
public FilterRegistrationBean filterRegistrationBean2() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter2());
registrationBean.setName("filter2");
//設置順序
registrationBean.setOrder(3);
return registrationBean;
}
@Bean
public Filter filter1() {
return new MyFilter();
}
@Bean
public Filter filter2() {
return new MyFilter2();
}
}
複製代碼
攔截器是Spring提出的概念,它的做用於過濾器相似,能夠攔截用戶請求並進行相應的處理,它能夠進行更加精細的控制。springboot
在SpringMVC中,DispatcherServlet捕獲每一個請求,在到達對應的Controller以前,請求能夠被攔截器處理,在攔截器中進行前置處理後,請求最終纔到達Controller。bash
攔截器的接口是 org.springframework.web.servlet.HandlerInterceptor
接口,接口代碼以下:服務器
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
複製代碼
接口方法解讀:ide
preHandle
方法返回 true。具體來講,postHandler
方法會在DispatcherServlet進行視圖返回渲染前被調用,也就是說咱們能夠在這個方法中對 Controller 處理以後的ModelAndView
對象進行操做preHandle
方法的返回值爲 true才行。該方法通常用於資源清理工做public class MyInterceptor implements HandlerInterceptor {
private Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("preHandle....");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("postHandle...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("afterCompletion...");
}
}
複製代碼
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(handlerInterceptor())
//配置攔截規則
.addPathPatterns("/**");
}
@Bean
public HandlerInterceptor handlerInterceptor() {
return new MyInterceptor();
}
}
複製代碼
在springMVC中咱們能夠實現多個攔截器,並依次將他們註冊進去,以下:
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(handlerInterceptor())
.addPathPatterns("/**");
registry.addInterceptor(handlerInterceptor2())
.addPathPatterns("/**");
}
複製代碼
攔截器的順序也跟他們註冊時的順序有關,至少 preHandle
方法是這樣,下圖表示了兩個攔截器協同工做時的執行順序:
上圖出自慕課網
後臺打印日誌也輸出了相同的執行順序:
io-9999-exec-2] c.p.filter.interceptor.MyInterceptor : preHandle....
2018-09-13 12:13:31.292 INFO 9736 --- [nio-9999-exec-2] c.p.filter.interceptor.MyInterceptor2 : preHandle2....
2018-09-13 12:13:31.388 INFO 9736 --- [nio-9999-exec-2] c.p.filter.controller.HelloController : username:pjmike,password:123456
2018-09-13 12:13:31.418 INFO 9736 --- [nio-9999-exec-2] c.p.filter.interceptor.MyInterceptor2 : postHandle2...
2018-09-13 12:13:31.418 INFO 9736 --- [nio-9999-exec-2] c.p.filter.interceptor.MyInterceptor : postHandle...
2018-09-13 12:13:31.418 INFO 9736 --- [nio-9999-exec-2] c.p.filter.interceptor.MyInterceptor2 : afterCompletion2...
2018-09-13 12:13:31.418 INFO 9736 --- [nio-9999-exec-2] c.p.filter.interceptor.MyInterceptor : afterCompletion...
複製代碼
從上面對攔截器與過濾器的描述來看,它倆是很是類似的,都能對客戶端發來的請求進行處理,它們的區別以下:
preHandle
方法內返回 false 進行中斷簡單總結一下,攔截器相比過濾器有更細粒度的控制,依賴於Spring容器,能夠在請求以前或以後啓動,過濾器主要依賴於servlet,過濾器能作的,攔截器基本上都能作。