Java中的過濾器和攔截器有什么區別么?
今天我們不談框架,我們來說說這個 Java 基礎中的過濾器和攔截器,為什么要談呢?其實就有粉絲會問了不起,這個過濾器和攔截器他們有什么區別呢?為什么面試官在面試的時候經常會問到這個過濾器和攔截器的區別,以及他們分別對應的應用場景呢?
今天了不起就來說說這個過濾器和攔截器。
過濾器
過濾器 Filter
什么是過濾器
過濾器是 Servlet 的高級特性之一,是實現 Filter 接口的 Java 類。其基本功能就是對 servlet 的調用進行干預,在 Servlet 請求和響應的過程中增加一些特定的功能。可以使用過濾器實現的功能有:統一編碼,URL級別的權限訪問控制,過濾敏感詞匯,壓縮請求信息.
我們來通過代碼來看 Filter 的執行過程
public interface Filter {
//用于執行過濾器的初始化工作,web容器會在web項目啟動時自動調用該方法
default void init(FilterConfig filterConfig) throws ServletException {
}
//當請求和響應被過濾器攔截后,都會交給doFilter來處理:其中兩個參數分別是被攔截request和response對象,可以使用chain的doFliter方法來放行
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
//用于釋放關閉Filter對象打開的資源,在web項目關閉時,由web容器自動調用該方法
default void destroy() {
}
}
在 Filter 中我們可以看到有三個方法,分別是
- init
- doFilter
- destroy
至于每個方法的作用,了不起已經把他們的功能寫在了注釋上面,他們實際上就是三個步驟:
1.初始化
2.處理請求和相應過濾,完成操作
3.最后釋放資源
而實現一個自定義 Filter 也是比較簡單的,我們來實現一個簡單的自定義的 Filter。
@Component
@Slf4j
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("初始化過濾器:" + filterConfig.getFilterName());
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 對請求進行過濾操作處理
log.info("MyFilter 開始對請求進去過濾操作!");
String requestURI = httpRequest.getRequestURI();
log.info("請求地址:" + requestURI);
// 繼續執行下一個 Filter,如果沒有其他 Filter 的話就執行 Controller 層代碼
long startTime = System.currentTimeMillis();
chain.doFilter(request, response);
// 執行完用戶請求后,回到這里對 response 響應內容做一些處理
long endTime = System.currentTimeMillis();
log.info("請求處理完畢,所花費時間為:" + (endTime - startTime) + "ms");
}
@Override
public void destroy() {
log.info("銷毀過濾器 MyFilter");
}
}
至于 Filter 的配置的話,了不起都不用解釋,現在幾乎沒有再去配置 xml 的方式了,都是配置類或者注解的方式。
注解方式:
@WebFilter(filterName = "myFilter",urlPatterns = {"/*"})
配置類方式:
@Configuration
public class MyFilterConfig {
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean fb = new FilterRegistrationBean();
//設置filter啟動順序
fb.setOrder(1);
fb.setFilter(new MyFilter());
fb.addInitParameter("phone","183****5510");
//設置攔截請求規則,這里攔截所有請求
fb.addUrlPatterns("/*");
return fb;
}
}
我們看完了這個過濾器,接下來就得看看這個攔截器了。
攔截器
攔截器 Interceptor
什么是攔截器
Spring MVC 中的攔截器Interceptor類似于 ServLet 中的過濾器Filter,它主要用于攔截用戶請求并作出相應的處理。例如通過攔截器可以進行權限驗證、記錄請求信息的日志、判斷用戶是否登錄等。
老規矩,直接看源代碼
public interface HandlerInterceptor {
//預處理方法,本方法在控制器方法(MyController的方法)之前執 行,用戶的請求最先到達此方法,在這個方法中可以獲取請求的信息,驗證請求是否符合要求。以驗證用戶是否登錄,驗證用戶是否有權限訪問某個鏈接地址(url)。如果返回true則放行,返回false則攔截
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
//后處理方法。在controller中的方法之后執行的。能夠獲取到處理器方法的返回值 mv,可以修改mv中的數據和視圖。可以影響到最后的執行結果。主要是對原來的執行結果做二次修正
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
//最后執行的方法,在頁面渲染之后執行。在請求處理完成后執行的,框架中規定是當你的視圖處理完成后,對視圖進行了forword。就任務請求處理完成。
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
其實也是三個步驟:
1.預處理
2.后處理
3.最后執行
至于每一步的含義和內容,了不起同樣的再注釋中表明了。
同樣的,自定義實現一個攔截器也很簡單:
public class MyInterceptor implements HandlerInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(MyInterceptor.class);
private static final ThreadLocal<Long> START_THREAD_LOCAL = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String uri = request.getRequestURI();
LOGGER.info(uri + " preHandle");
Long startTime = System.currentTimeMillis(); //獲取開始時間
START_THREAD_LOCAL.set(startTime); //線程綁定變量(該數據只有當前請求的線程可見)
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
String uri = request.getRequestURI();
LOGGER.info(uri + " postHandle");
Long startTime = START_THREAD_LOCAL.get();//得到線程綁定的局部變量(開始時間)
Long endTime = System.currentTimeMillis(); //2、結束時間
Long time = endTime - startTime;
LOGGER.info("http request all time: " + time + "ms");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) throws Exception {
String uri = request.getRequestURI();
LOGGER.info(uri + " afterCompletion");
if (START_THREAD_LOCAL != null) {
START_THREAD_LOCAL.remove(); // 移除ThreadLocal中的局部變量
}
}
}
我們自定義一個獲取并返回某個靜態資源的內容以及整個請求所花費的時間攔截器,一般這個還是比較有用的,而且還可以加一個請求訪問的,然后來處理方法執行時間的。
配置攔截器其實在項目中是非常的常見。
@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
/**
* 配置攔截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(myInterceptor())
.addPathPatterns("/api/v1/**")//需要攔截的請求
.addPathPatterns("/api/v1/**")//需要攔截的請求
.excludePathPatterns("/api/debug/**")//不攔截的請求
.excludePathPatterns("api/page/getName");//不攔截的請求
}
/**
* 注入攔截器到spring容器
* @return
*/
@Bean
public MyInterceptor myInterceptor(){
return new MyInterceptor();
}
}
我們看完了過濾器和攔截器的實現,接下來就得來看看這個過濾器和攔截器之間的區別了。
1.依賴點不同
- 過濾器依賴與servlet容器,而攔截器不依賴與servlet容器
- 過濾器屬于Servlet,而攔截器屬于springmvc
2.實現原理不同
- 過濾器是基于函數回調,而攔截器是基于java的反射機制的
3.作用域不同
- 過濾器則可以對幾乎所有的請求起作用,而攔截器只能對action請求起作用
4.生命周期不同
- 在action的生命周期中,攔截器可以多次被調用,而過濾器只能在容器初始化時被調用一次
這是區別,其實二者的相同點也是有的,比如,攔截器和過濾器實際上都是 Spring 中 AOP 的體現,都能實現一些權限和日志方面的功能。
你知道他們是什么區別了么?