引言
說起 Filter 與 Interceptor 的區別,相信很多同學第一感覺就是容易、簡單!
畢竟開發中這兩個元件使用頻率較高,用法也較簡單。然後真回答起來有答不出個所以然來,場面尷尬?,老丟臉了!
看著簡單,一答就錯,下面我們們先看結論!再做詳細解說!
結論
- 底層原理不同:Filter 是 基於 函式回撥 實現的; Interceptor 是基於 反射機制與動態代理 實現的。
- 使用範圍不同:Filter 是 Servlet規範 的介面,依賴web容器(Tomcat等),只能在web工程中使用;Interceptor 是 Spring的元件,不依賴web容器。
- 觸發時機不同:請求進入順序: Tomcat ==> Filter ==> Servlet ==> Interceptor ==> Controller。
- 攔截範圍不同:Filter 對進入容器的所有請求進行攔截;Interceptor 只會對
Controller
中請求或訪問static
目錄下的資源請求進行攔截。 - 注入bean情況不同:Filter 中能正常注入其他bean; Interceptor 在 springcontext 之前載入,而 bean 由 Spring管理,所以註冊 Interceptor 前需要先手動注入 Interceptor ;
- 控制執行順序不同:實際開發中,使用的通常是多個 Filter 或 Interceptor 組成的 鏈;Filter 中 攔截的核心方法是 doFilter(), Filter 直接按順序執行;但是在 Interceptor 中存在 前置攔截方法 preHandle() 和 後置攔截方法 postHandle(),preHandle() 是順序執行的,而 postHandle() 是反順序執行的。
原理
函式回撥
函式回撥,簡稱回撥(callback),是指透過函式引數傳遞到其它程式碼的,某一塊可執行程式碼的引用。
Java中沒有指標,不能將函式名作為引數傳遞,只能透過反射、直接呼叫、介面呼叫、Lambda表示式等方法來實現函式回撥。這裡用Lambda表示式給大家做個演示:
請求類:
public class Request{
public void send(CallBack callBack) {
System.out.println("[Request]:傳送請求");
}
}
回撥介面:
public interface CallBack {
void processResponse();
}
測試類:
public class Main {
public static void main(String[] args) {
Request request = new Request();
request.send(()-> System.out.println("[CallBack]:監聽到請求,進行處理響應"));
}
}
注:想看看回撥其他寫法的可以看看這篇文章:Java回撥的四種寫法(反射、直接呼叫、介面呼叫、Lamda表示式) - 騰訊雲開發者社群-騰訊雲
過濾器Filter 與 攔截器 Interceptor 原理
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(servletRequest, servletResponse);
}
}
這是一個自定義的過濾器,doFilter()方法中傳入了一個介面引數FilterChain,這就是一個介面呼叫的函式回撥。FilterChain介面中就只有一個回撥方法doFilter()。
Interceptor 的原理就是一個jdk的動態代理,這裡就不作演示了。
Interceptor 注入其他bean
實際開發中,通常透過實現 HandlerInterceptorAdapter 來自定義攔截器,而不是直接使用 HandlerInterceptor。
- 造成testService為null的原因就是攔截器比springcontext先載入,從下面的程式碼中也可以看到,攔截器是手動直接加入到登錄檔表中的,所以使用 @Bean 註解又手動注入了一次攔截器。此時攔截器中就可以注入其他bean了。
@Configuration
public class GlobalWebAppConfigurer implements WebMvcConfigurer {
/**
* 將攔截器新增到登錄檔中
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor()).addPathPatterns("/**");
}
// 手動注入攔截器
@Bean
public MyInterceptor myInterceptor(){
return new MyInterceptor();
}
}
Interceptor 執行順序
由spring mvc的原始碼決定的,在核心轉發器 DispatcherServlet 的 doDispatch 中,applyPreHandle()、
applyPostHandle()對攔截器陣列的呼叫順序是相反的。具體原始碼等寫到springmvc再分析。