過濾器Filter
定義
對Servlet容器調用Servlet的過程進行攔截,基于函數回調實作
常見使用場景
- 統一設定編碼
- 過濾敏感字元
- 登入校驗
- URL級别的通路權限控制
- 資料壓縮
使用方式
這裡展示的是SpringBoot整合過濾器的方式
使用配置類的方式(bean注入)
- 編寫filter類并實作Filter接口
- 實作Filter接口中的init,doFilter方法,與destory方法
- doFilter方法中使用chain.doFilter(request, response);來放行servlet
- 編寫Filter配置類,注解加上@ConFiguration注解
- 編寫方法,傳回值類型為FilterRegistrationBean
- 方法内建立FilterRegistrationBean對象,構造器内傳入過濾器類的對象,直接new一個就可以,或者讓Spring容器自動注入後再使用
- 調用FilterRegistrationBean 對象的addUrlPatterns方法類設定過濾的請求路徑
過濾器類
@Slf4j//友善使用下面的log.info()方法輸出日志
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
log.info("過濾器{}正在生效",LoginCheckFilter.class.getName());
log.info("過濾器{}攔截到了請求{}",LoginCheckFilter.class.getName(),request.getRequestURI());
filterChain.doFilter(request,response);
log.info("請求{}已經被放行",request.getRequestURI());
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("過濾器{}被初始化",LoginCheckFilter.class.getName());
Filter.super.init(filterConfig);
}
@Override
public void destroy() {
log.info("過濾器{}被銷毀",LoginCheckFilter.class.getName());
Filter.super.destroy();
}
}
配置類
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LoginCheckFilter> filterRegistrationBean() {
FilterRegistrationBean<LoginCheckFilter> loginCheckFilter = new FilterRegistrationBean<>();
loginCheckFilter.setFilter(new LoginCheckFilter());//注冊自定義過濾器
loginCheckFilter.setName("LoginCheckFilter");//過濾器名稱
loginCheckFilter.addUrlPatterns("/*");//過濾所有路徑
loginCheckFilter.setOrder(1);//優先級,最頂級按照資料從小到大來執行過濾器
return loginCheckFilter;
}
}
使用注解
- 建立過濾器類,方法與上面建立過濾器類的步驟一樣
- 過濾器類上面添加注解 @WebFilter(urlPatterns = {“攔截路徑”}, filterName = “過濾器名稱”)
- 在啟動類上添加@ServeltComponentScan注解來掃描servlet元件(包括:@WebFilter和@WebListener)
注意:使用注解方式無法控制多個過濾器的順序,()預設執行順序是按照類名字母的排序,
使用@Order注解控制Bean的執行順序時,必須配合注解一起使用,才會起作用
但是@Component與@WebFilter注解都會把過濾器類加載一遍,并且@component會把@WebFilter加載的Bean覆寫掉,進而使得@WebFilter配置的攔截路徑與過濾器名稱失效,
是以如果指定了優先級則配置的攔截路徑失效,預設為任意路徑,如果不使用@Order注解則無法控制多個過濾器的執行順序,
綜上筆者這裡推薦使用Bean注入的方式來使用過濾器
總結
- Filter是依賴于Servlet的,需要導入Servlet的依賴
- 過濾器會在容器啟動時被初始化,并且隻會初始化一次
- doFilter()方法在目标請求被攔截前執行,放行調用filterChain.doFilter(servletRequest,servletResponse);具體變量名根據方法體而變
- destroy()方法在容器銷毀時執行,隻執行一次
- Filter可以攔截所有請求,包括靜态資源
- 過濾器基于函數回調實作
擴充
- 過濾器,監聽器和Servlet是JavaWeb三大元件之一,它們的在SpringBoot中的使用方式都差不多差別如下
- 使用注解方式使用時都需要在啟動類加上@ServletComponentScan注解
- 三個注解為@WebSrvlet,@WebFilter和@Weblistener
- Servlet類繼承的是HttpServlet,Filter實作的是Filter類,Listener實作ServletContextListener類
- 使用Bean注入方式使用時,Filter元件方法傳回類型是FilterRegistrationBean,Listener元件方法傳回類型為ServletListenerRefistrationBean,Servlet元件傳回類型為ServletRefistrationBean
攔截器Interceptor
定義
類似于Servlet中的過濾器,主要用于攔截使用者請求并做相應的處理,基于java反射機制(動态代理)實作
常見使用場景
- 日志記錄
- 權限校驗
- 登入校驗
- 性能檢測【檢測方法的執行時間】
其實攔截器和過濾器很像,有些使用場景。無論選用誰都能實作。需要注意的使他們彼此的使用範圍,觸發機制。
使用方式
- 編寫攔截器類并實作HandlerInterceptor 接口
- 實作HandlerInterceptor 接口的preHandle,postHandle和afterHandle方法
- 編寫配置類,注意加上@configuration注解,并繼承WebMvcConfigurer接口
總結
- 攔截器依賴于SpringMvc的,需要導入Mvc的依賴
- preHandle() 在目标請求完成之前執行。有傳回值Boolean類型,true:表示放行
- postHandle() 在目标請求之完成後執行。
- afterCompletion() 在整個請求完成之後【modelAndView已被渲染執行】。
- 攔截器隻能攔截action請求,不包括靜态資源(有待驗證)
- 基于java反射機制實作
攔截器不生效原因分析
- 配置類沒有加@configuration注解
- 攔截路徑出錯,/*攔截目前目錄,/**攔截所有目錄
- WebMvcConfigurationSupport,WebMvcConfigurer,WebMvcConfigurerAdapter項目中不能有兩個以上的類繼承或實作自這三個類 因為這三個類都是WebMVC的配置類,如果同時出現隻會有一個類生效,一般實作WebMvcConfilgurer就可以了
過濾器與攔截器的差別
- 觸發順序不一樣:
- 過濾器的觸發順序在攔截器前,也就是先執行過濾器,在執行攔截器, 過濾前 - 攔截前 - Action處理 - 攔截後 - 過濾後。
- 過濾器之間的執行順序嵌套的。
- 但是攔截器執行順序也是嵌套執行的但是,因為有三個方法,會先執行pre方法,再一起執行post方法,最後一起執行after方法。
- 實作方式不同,過濾器基于函數回調實作,攔截器基于動态代理(反射)實作。
- 攔截器是基于java的反射機制的,而過濾器是基于函數回調。
- 攔截器不依賴與servlet容器,過濾器依賴與servlet容器。
- 攔截器隻能對action請求起作用,而過濾器則可以對幾乎所有的請求起作用。
- 攔截器可以通路action上下文、值棧裡的對象,而過濾器不能通路。
- 在action的生命周期中,攔截器可以多次被調用,而過濾器隻能在容器初始化時被調用一次。
- 攔截器可以擷取IOC容器中的各個bean,而過濾器就不行,這點很重要,在攔截器裡注入一個service,可以調用業務邏輯。
aop與過濾器,攔截器的差別
- 過濾器,攔截器攔截的是URL。AOP攔截的是類的中繼資料(包、類、方法名、參數等)。
- 過濾器并沒有定義業務用于執行邏輯前、後等,僅僅是請求到達就執行。
- 攔截器有三個方法,相對于過濾器更加細緻,有被攔截邏輯執行前、後等。
- AOP針對具體的代碼,能夠實作更加複雜的業務邏輯。
- 三者功能類似,但各有優勢,從過濾器 -> 攔截器 -> 切面,攔截規則越來越細緻。
- 執行順序依次是過濾器、攔截器、切面。