springboot2.x基礎(chǔ)教程:過濾器和攔截器詳解

在springboot web項目開發(fā)過程中,我們通常需要對請求與響應(yīng)的內(nèi)容請求攔截處理,如進行請求日志記錄表鳍、UA檢查、用戶權(quán)限驗證祥诽、非法內(nèi)容過濾等功能譬圣,這時候過濾器與攔截器就派上用場。
本文帶大家講解springboot如何使用過濾器與攔截器以及兩者之間的區(qū)別原押。

過濾器

Servlet 過濾器可以動態(tài)地攔截請求和響應(yīng)胁镐,以變換或使用包含在請求或響應(yīng)中的信息。過濾器是一個實現(xiàn)了 javax.servlet.Filter 接口的 Java 類诸衔。javax.servlet.Filter 接口定義了三個方法:

public interface Filter {
    default void init(FilterConfig filterConfig) throws ServletException {
    }
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
    default void destroy() {
    }
}
序號 方法&描敘
1 doFilter
該方法完成實際的過濾操作盯漂,當(dāng)客戶端請求方法與過濾器設(shè)置匹配的URL時,Servlet容器將先調(diào)用過濾器的doFilter方法笨农。FilterChain用戶訪問后續(xù)過濾器就缆。
2 init
web 應(yīng)用程序啟動時,web 服務(wù)器將創(chuàng)建Filter 的實例對象谒亦,并調(diào)用其init方法竭宰,讀取web.xml配置空郊,完成對象的初始化功能,從而為后續(xù)的用戶請求作好攔截的準(zhǔn)備工作(filter對象只會創(chuàng)建一次切揭,init方法也只會執(zhí)行一次)狞甚。開發(fā)人員通過init方法的參數(shù),可獲得代表當(dāng)前filter配置信息的FilterConfig對象廓旬。
3 destroy
Servlet容器在銷毀過濾器實例前調(diào)用該方法哼审,在該方法中釋放Servlet過濾器占用的資源。

SpringBoot使用過濾器

定義了一個簡單的過濾器

@Slf4j
public class LogFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
         HttpServletRequest req=(HttpServletRequest)servletRequest;
         log.info(req.getRequestURI());
         filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}

使用FilterRegistrationBean注冊過濾器

@Configuration
public class LogFilterConfiguration {
    @Bean
    public FilterRegistrationBean registrationBean(){
        FilterRegistrationBean registrationBean=new FilterRegistrationBean();
        registrationBean.setFilter(new LogFilter());
        //匹配的過濾器
        registrationBean.addUrlPatterns("/*");
        //過濾器名稱
        registrationBean.setName("logFilter");
        //過濾器順序
        registrationBean.setOrder(1);
        return registrationBean;
    }
}

使用Servlet3.0注解定義過濾器

@WebFilter(urlPatterns = "/*",filterName = "authFiler")
@Slf4j
public class AuthFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        log.info("進行權(quán)限校驗.........");
        chain.doFilter(servletRequest,servletResponse);
    }

}

兩種方式的區(qū)別:

  1. WebFilter這個注解并沒有指定執(zhí)行順序的屬性孕豹,其執(zhí)行順序依賴于Filter的名稱涩盾,是根據(jù)Filter類名(注意不是配置的filter的名字)的字母順序倒序排列
  2. @WebFilter指定的過濾器優(yōu)先級都高于FilterRegistrationBean配置的過濾器
  3. FilterRegistrationBean方式可以注入SpringBoot IOC容器中的Bean

攔截器

SpringBoot攔截器Interceptor類似 面向切面編程 中的 切面通知,我們通過 動態(tài)代理 對一個 service() 方法添加 通知 進行功能增強励背。比如說在方法執(zhí)行前進行 初始化處理春霍,在方法執(zhí)行后進行 后置處理攔截器 的思想和 AOP 類似叶眉,區(qū)別就是 攔截器 只能對 ControllerHTTP 請求進行攔截址儒。

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 {
    }
}
序號 方法&描敘
1 preHandle
該方法在controller接收請求處理request之前執(zhí)行,返回值為 boolean竟闪,返回值為 true 時接著執(zhí)行 postHandle() 和 afterCompletion() 方法离福;如果返回false則中斷執(zhí)行。
2 postHandle
在 controller 處理請求之后炼蛤, ModelAndView 處理前執(zhí)行妖爷,可以對 響應(yīng)結(jié)果 進行修改。
3 afterCompletion
在 DispatchServlet 對本次請求處理完成理朋,即生成 ModelAndView 之后執(zhí)行絮识。

定義一個簡單的攔截器

@Slf4j
public class LogHandler implements HandlerInterceptor {
    private NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<>("StopWatch-StartTime");

    public LogHandler() {
        super();
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        long beginTime = System.currentTimeMillis();//1、開始時間      
        startTimeThreadLocal.set(beginTime);//線程綁定變量(該數(shù)據(jù)只有當(dāng)前請求的線程可見)
        return true;//繼續(xù)流程
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        long endTime = System.currentTimeMillis();
        long beginTime = startTimeThreadLocal.get();//得到線程綁定的局部變量(開始時間)  
        long consumeTime = endTime - beginTime;
        //3嗽上、消耗的時間         
        log.info(String.format("%s consume %d millis", request.getRequestURI(), consumeTime));
    }    
}

注冊攔截器

@Configuration
public class HandlerConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogHandler());
    }
}

過濾器與攔截器區(qū)別

序號 區(qū)別
1 Filter是servlet規(guī)范次舌,使用范圍是web程序,攔截器不限于web程序兽愤,也可以用于Application彼念、Swing程序中
2 Filter是servlet規(guī)范中定義,是servlet容器支持的浅萧。攔截器是Spring容器內(nèi)逐沙,是spring框架支持的
3 攔截器是Spring的一個組件,能夠使用spring中對象洼畅,如Service對象吩案、數(shù)據(jù)源、事務(wù)管理帝簇、通過IOC注入容器即可徘郭,filter則不能
4 filter在servlet前后起作用靠益,攔截器能夠深入方法的前后,異常拋出前后残揉。
5 在springboot項目中一般優(yōu)先使用攔截器

千里之行胧后,始于足下。這里是SpringBoot教程系列第十二篇抱环,所有項目源碼均可以在我的GitHub上面下載源碼绩卤。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市江醇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌何暇,老刑警劉巖陶夜,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異裆站,居然都是意外死亡条辟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門宏胯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來羽嫡,“玉大人,你說我怎么就攤上這事肩袍『伎茫” “怎么了?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵氛赐,是天一觀的道長魂爪。 經(jīng)常有香客問我,道長艰管,這世上最難降的妖魔是什么滓侍? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮牲芋,結(jié)果婚禮上撩笆,老公的妹妹穿的比我還像新娘。我一直安慰自己缸浦,他們只是感情好夕冲,可當(dāng)我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著餐济,像睡著了一般耘擂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上絮姆,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天醉冤,我揣著相機與錄音秩霍,去河邊找鬼。 笑死蚁阳,一個胖子當(dāng)著我的面吹牛铃绒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播螺捐,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼颠悬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了定血?” 一聲冷哼從身側(cè)響起赔癌,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎澜沟,沒想到半個月后灾票,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡茫虽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年刊苍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片濒析。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡正什,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出号杏,到底是詐尸還是另有隱情婴氮,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布馒索,位于F島的核電站莹妒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏绰上。R本人自食惡果不足惜旨怠,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜈块。 院中可真熱鬧鉴腻,春花似錦、人聲如沸百揭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽器一。三九已至课锌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背渺贤。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工雏胃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人志鞍。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓瞭亮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親固棚。 傳聞我的和親對象是個殘疾皇子统翩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,860評論 2 361