淺析SpringBoot中的過濾器和攔截器
介紹
過濾器和攔截器都是為了在請(qǐng)求到達(dá)目標(biāo)處理器(Servlet或Controller)之前或者之后插入自定義的處理邏輯
過濾器:
遵循AOP(面向切面編程)思想實(shí)現(xiàn),基于Servlet規(guī)范提供的Filter接口,它是位于客戶端請(qǐng)求與服務(wù)器響應(yīng)之間的一個(gè)組件,依賴于Servlet容器。當(dāng)請(qǐng)求到達(dá)服務(wù)器時(shí),過濾器會(huì)在請(qǐng)求進(jìn)入實(shí)際目標(biāo)資源(如Servlet、JSP頁面)之前或之后執(zhí)行特定的操作,原理是基于函數(shù)回調(diào)
攔截器
遵循AOP(面向切面編程)思想實(shí)現(xiàn),如Spring MVC中的HandlerInterceptor
接口,它不依賴于Servlet容器的具體實(shí)現(xiàn),而是由應(yīng)用框架管理。攔截器是在請(qǐng)求進(jìn)入到控制器層(Controller)方法前后執(zhí)行自定義邏輯
原理解析
過濾器
為什么說過濾器基于函數(shù)回調(diào)?
過濾器基于函數(shù)回調(diào),所謂函數(shù)回調(diào)/回調(diào)函數(shù)指的是:一個(gè)函數(shù)(稱為回調(diào)函數(shù))作為參數(shù)傳遞給另一個(gè)函數(shù)(稱為調(diào)用函數(shù)),當(dāng)滿足一定條件或者在某個(gè)特定時(shí)刻,調(diào)用函數(shù)會(huì)調(diào)用傳遞過來的回調(diào)函數(shù)
由于Java中不直接支持函數(shù)指針,所以常常通過接口來實(shí)現(xiàn)回調(diào)機(jī)制
FilterChain
就是一個(gè)接口
public interface FilterChain { public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException; }
Filter
的實(shí)現(xiàn)類中doFilter()
方法中FilterChain
作為參數(shù)被傳進(jìn)來,并且在合適的時(shí)機(jī)被回調(diào)了其doFilter
方法
public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { Filter.super.init(filterConfig); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { log.info("hello"); chain.doFilter(request,response); } @Override public void destroy() { Filter.super.destroy(); } }
攔截器
攔截器基于AOP(面向切面編程)思想實(shí)現(xiàn),但是并不一定用到動(dòng)態(tài)代理或者切面,切點(diǎn)之類的技術(shù),以如Spring MVC中的HandlerInterceptor
接口為例,從源碼看更像是直接將攔截器注入,形成了一個(gè)攔截器鏈,在controller
層面上進(jìn)行代碼織入
DispatcherServlet
作為SpringMVC框架的核心類,http請(qǐng)求的核心執(zhí)行方法為doService()
,再進(jìn)入doDispatch()
方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; /** * 1. * HandlerExecutionChain 是一個(gè)對(duì)象 * 包含了以下重要的屬性 * private final Object handler; //處理器(controller和其最后的方法) * List<HandlerInterceptor> interceptorList = new ArrayList<>();//攔截器列表,用來存儲(chǔ)匹配處理器的攔截器 */ HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); /** * 2. * 下面這行代碼 大概做了以下事情 * 1.通過request和url 匹配了對(duì)應(yīng)的controller以及調(diào)用的方法 填充了HandlerExecutionChain.handler * 2.通過匹配request和HandlerInterceptor的注冊(cè)信息(攔截哪些,放行哪些),往HandlerExecutionChain.interceptorList中添加對(duì)應(yīng)的攔截器 */ mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = HttpMethod.GET.matches(method); if (isGet || HttpMethod.HEAD.matches(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } /** * 3.執(zhí)行 攔截器鏈條中的所有前置方法 */ if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } /** * 4.交由處理器(controller對(duì)應(yīng)的方法)去處理方法中的業(yè)務(wù)邏輯 */ mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); /** * 5.倒序執(zhí)行 攔截器鏈條中的所有后置方法 */ mappedHandler.applyPostHandle(processedRequest, response, mv); }} //...
流程解析
由于Filter依賴于Servlet容器所以不同的容器Filter,FilterChain的實(shí)現(xiàn)類存在差異,這里以Tomcat為例分析
1.向后臺(tái)發(fā)起一次請(qǐng)求
2.接待線程接收到,將任務(wù)轉(zhuǎn)交給工作線程
3.判斷協(xié)議,封裝必要對(duì)象
4.將request,response一路轉(zhuǎn)交至StandardWrapperValve.invoke(Request request, Response response)
5.創(chuàng)建過濾鏈ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
? 5.1從上下文中獲取注冊(cè)好的過濾器
? 5.2遍歷過濾器,匹配URL,Servlet等,將匹配好的過濾器加入到過濾器鏈
6.依次回調(diào)FilterChain的doFilter方法filterChain.doFilter(request.getRequest(),response.getResponse());
7.將所有過濾器的前置代碼執(zhí)行完畢,進(jìn)入servlet,servlet.service(request, response);
8.進(jìn)入DispatcherServlet
統(tǒng)一調(diào)度
9.調(diào)用攔截器前置方法
10.進(jìn)入controller中的對(duì)應(yīng)方法,執(zhí)行具體的業(yè)務(wù)邏輯
11.調(diào)用攔截器后置(數(shù)組倒序執(zhí)行)
12.將所有過濾器的后置代碼執(zhí)行完畢(方法棧,先進(jìn)后出)
13.將結(jié)果返回給請(qǐng)求者
注意,過濾器和攔截器實(shí)現(xiàn)先進(jìn)后出的實(shí)現(xiàn)方式是不同的,過濾器基于函數(shù)回調(diào),方法棧結(jié)構(gòu)天生支持先進(jìn)后出;攔截器則是直接使用循環(huán)倒序遍歷
總結(jié)
同:
過濾器和攔截器都遵循面向切面編程的思想(AOP),實(shí)現(xiàn)了在請(qǐng)求到達(dá)目標(biāo)處理器(Servlet/Controller)之前或者之后插入自定義的處理邏輯
異:
1.使用范圍不同
- 過濾器實(shí)現(xiàn)的是
javax.servlet.Filter
該接口在Servlet
規(guī)范中定義,依賴WEB容器 - 攔截器是一個(gè)Spring組件,由Spring管理,并不依賴Tomcat容器,可以單獨(dú)使用(Application,Swing)
2.使用的場(chǎng)景不同
- 攔截器更加接近業(yè)務(wù)系統(tǒng),所以攔截器更適用于處理統(tǒng)一的業(yè)務(wù)邏輯,比如權(quán)限判斷等
- 過濾器通常用來實(shí)現(xiàn)通用功能,比如xss過濾,敏感詞,處理跨域等等
3.觸發(fā)的時(shí)機(jī)不同
過濾器的觸發(fā)時(shí)機(jī)早于攔截器
4.底層實(shí)現(xiàn)細(xì)節(jié)不同
- 過濾器實(shí)現(xiàn)先進(jìn)后出基于方法棧的數(shù)據(jù)結(jié)構(gòu)
- 攔截器實(shí)現(xiàn)先進(jìn)后出基于循環(huán)倒序遍歷
到此這篇關(guān)于淺析SpringBoot中的過濾器和攔截器的文章就介紹到這了,更多相關(guān)SpringBoot過濾器和攔截器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文詳細(xì)springboot實(shí)現(xiàn)MySQL數(shù)據(jù)庫的整合步驟
Spring Boot可以很方便地與MySQL數(shù)據(jù)庫進(jìn)行整合,下面這篇文章主要給大家介紹了關(guān)于springboot實(shí)現(xiàn)MySQL數(shù)據(jù)庫的整合步驟,文中通過圖文以及代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-03-03IntelliJ IDEA運(yùn)行bat腳本,自動(dòng)taskkill端口進(jìn)程
這篇文章主要介紹了IDEA里面無法運(yùn)行bat文件的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11解決Springboot get請(qǐng)求是參數(shù)過長(zhǎng)的情況
這篇文章主要介紹了解決Springboot get請(qǐng)求是參數(shù)過長(zhǎng)的情況,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09Mybatis-Plus中IdType.AUTO局部配置不生效的問題解決
本文主要介紹了Mybatis-Plus中IdType.AUTO局部配置不生效的問題解決,數(shù)據(jù)庫插入數(shù)據(jù)時(shí),id的默認(rèn)生成方式還是雪花算法,局部配置沒有生效,下面就來解決一下,感興趣的可以了解一下2023-09-09Spring Cloud 系列之服務(wù)調(diào)用 OpenFeign的實(shí)現(xiàn)
這篇文章主要介紹了Spring Cloud 系列之服務(wù)調(diào)用 OpenFeign的實(shí)現(xiàn),需要的朋友可以參考下2020-11-11Java中避免NullPointerException的方法總結(jié)
這篇文章主要介紹了Java中避免NullPointerException的方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-07-07解決Spring事務(wù)@Transactional多層嵌套失效問題
在使用Spring進(jìn)行事務(wù)管理時(shí),可能會(huì)遇到事務(wù)失效的問題,主要原因包括數(shù)據(jù)庫不支持事務(wù)、方法訪問級(jí)別不是public、未被Spring管理的Bean、當(dāng)前類的方法內(nèi)部調(diào)用以及配置的事務(wù)傳播性不當(dāng)?shù)?解決事務(wù)失效的方法有使用聲明式事務(wù)處理采用合適的事務(wù)傳播行為2024-11-11