一文詳解Spring攔截鏈的實(shí)現(xiàn)原理
在 Web應(yīng)用開發(fā)中,攔截器(Interceptor)是一種非常重要的機(jī)制,能夠在請(qǐng)求處理的各個(gè)階段進(jìn)行前置和后置處理。Spring框架提供了強(qiáng)大且靈活的攔截器機(jī)制,使開發(fā)者能夠輕松地在請(qǐng)求處理流程中插入自定義邏輯。
理解Spring攔截鏈的實(shí)現(xiàn)原理,不僅有助于我們更好地使用Spring提供的功能,還能讓我們?cè)谛枰獣r(shí)自定義復(fù)雜的請(qǐng)求處理邏輯。
1. 什么是攔截鏈?
簡(jiǎn)單來說,攔截鏈?zhǔn)且粋€(gè)處理請(qǐng)求的攔截器列表,按照一定的順序,一個(gè)一個(gè)地?cái)r截并處理請(qǐng)求。每個(gè)攔截器都可以在請(qǐng)求處理前、處理后或完成后執(zhí)行一些邏輯。
比如,我們可能需要在所有請(qǐng)求處理前進(jìn)行權(quán)限驗(yàn)證,在處理后記錄日志,或者在請(qǐng)求完成后釋放資源。這些操作可以通過定義不同的攔截器來實(shí)現(xiàn),每個(gè)攔截器負(fù)責(zé)一個(gè)特定的任務(wù)。
2. Spring中的攔截鏈
在Spring MVC中,攔截器鏈?zhǔn)峭ㄟ^HandlerInterceptor
接口及其實(shí)現(xiàn)類來實(shí)現(xiàn)的。Spring的DispatcherServlet
作為前端控制器(Front Controller),負(fù)責(zé)協(xié)調(diào)請(qǐng)求的各個(gè)階段,包括調(diào)用攔截器。
攔截器鏈的實(shí)現(xiàn)允許多個(gè)攔截器按照一定的順序?qū)φ?qǐng)求進(jìn)行處理。每個(gè)攔截器都有機(jī)會(huì)在請(qǐng)求處理前后執(zhí)行特定的邏輯,這為我們?cè)谡?qǐng)求處理流程中插入自定義邏輯提供了極大的靈活性。
3. 攔截鏈的核心組件
要理解攔截鏈的實(shí)現(xiàn)原理,首先需要了解 Spring MVC中幾個(gè)核心組件的作用和互相之間的關(guān)系:
HandlerMapping:HandlerMapping
負(fù)責(zé)將請(qǐng)求URL映射到具體的處理器(Handler)。處理器通常是一個(gè)控制器(Controller)的方法。Spring提供了多種HandlerMapping
實(shí)現(xiàn),如RequestMappingHandlerMapping
,支持基于注解的映射。
HandlerAdapter:HandlerAdapter
是負(fù)責(zé)執(zhí)行具體處理器的組件。它知道如何調(diào)用特定類型的處理器,并返回一個(gè)ModelAndView
對(duì)象,用于渲染視圖。
DispatcherServlet:DispatcherServlet
是Spring MVC的核心組件,充當(dāng)前端控制器。它接收所有的HTTP請(qǐng)求,協(xié)調(diào)HandlerMapping
、HandlerAdapter
和視圖解析等組件,最終將請(qǐng)求分發(fā)給合適的處理器進(jìn)行處理。
HandlerInterceptor:HandlerInterceptor
接口定義了攔截器的基本行為。通過實(shí)現(xiàn)該接口,可以在請(qǐng)求處理的不同階段插入自定義邏輯,如請(qǐng)求前、請(qǐng)求后或完成后的處理。
4. 攔截鏈的工作流程
了解了核心組件后,我們來看攔截鏈?zhǔn)侨绾卧谶@些組件間協(xié)作的。
- 請(qǐng)求到達(dá) DispatcherServlet:所有的HTTP請(qǐng)求首先由
DispatcherServlet
接收。 - 查找 Handler:
DispatcherServlet
使用HandlerMapping
查找與請(qǐng)求URL匹配的處理器(Handler)。 - 應(yīng)用攔截器前置:在調(diào)用處理器之前,
DispatcherServlet
會(huì)調(diào)用已注冊(cè)的所有攔截器的preHandle
方法。這些攔截器按照定義的順序依次執(zhí)行。如果任意一個(gè)攔截器的preHandle
返回false
,請(qǐng)求將被終止,后續(xù)的攔截器和處理器將不會(huì)執(zhí)行。 - 調(diào)用 HandlerAdapter 執(zhí)行 Handler:所有前置攔截器的
preHandle
方法返回true
后,DispatcherServlet
會(huì)調(diào)用HandlerAdapter
執(zhí)行具體的處理器方法(如Controller中的方法)。 - 應(yīng)用攔截器后置:處理器執(zhí)行完成后,
DispatcherServlet
會(huì)調(diào)用攔截器的postHandle
方法,這些攔截器按照定義的順序逆序執(zhí)行。 - 渲染視圖:
DispatcherServlet
使用視圖解析器(ViewResolver)渲染最終的視圖,如返回一個(gè)HTML頁(yè)面。 - 完成攔截器:最后,
DispatcherServlet
調(diào)用攔截器的afterCompletion
方法,通知攔截器請(qǐng)求已經(jīng)完成,同樣按逆序執(zhí)行。
這個(gè)流程確保了攔截器可以在請(qǐng)求處理的不同階段插入邏輯,例如驗(yàn)證、日志記錄、性能監(jiān)控等。
5. 源碼分析
要深入理解攔截鏈的實(shí)現(xiàn)原理,我們需要查看Spring MVC的源碼。下面,我們將逐步分析DispatcherServlet
、HandlerMapping
、HandlerAdapter
和HandlerInterceptor
等組件的源碼,實(shí)現(xiàn)對(duì)攔截鏈的全面理解。
5.1 DispatcherServlet的角色
DispatcherServlet
是整個(gè)Spring MVC請(qǐng)求處理流程的中央樞紐。它承擔(dān)了接收請(qǐng)求、查找處理器、執(zhí)行攔截器、調(diào)用處理器、渲染視圖等任務(wù)。
讓我們看看DispatcherServlet
中與攔截鏈相關(guān)的關(guān)鍵源碼。
public class DispatcherServlet extends FrameworkServlet { // ...其他代碼... @Override protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { // ...初始化上下文... // 1. 查找處理器 HandlerExecutionChain mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(request, response); return; } HttpServletRequest webRequest = createWebRequest(request, response); try { // 2. 應(yīng)用攔截器的preHandle方法 HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); if (!applyPreHandle(processedRequest, response, mappedHandler, interceptors)) { return; } // 3. 調(diào)用處理器 ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // 4. 應(yīng)用攔截器的postHandle方法 applyPostHandle(processedRequest, response, mappedHandler, mv, interceptors); // 5. 渲染視圖 render(mv, request, response); // 6. 應(yīng)用攔截器的afterCompletion方法 triggerAfterCompletion(processedRequest, response, mappedHandler, null, interceptors); } catch (Exception ex) { // 異常處理 // ... } } // ...其他代碼... }
從上面的代碼可以看出,DispatcherServlet
在處理請(qǐng)求的過程中,依次執(zhí)行了查找處理器、應(yīng)用攔截器的preHandle
、調(diào)用處理器、應(yīng)用攔截器的postHandle
、渲染視圖以及應(yīng)用攔截器的afterCompletion
方法的步驟。
5.2 HandlerMapping的查找機(jī)制
HandlerMapping
用于將請(qǐng)求映射到具體的處理器。Spring MVC提供了多種HandlerMapping
實(shí)現(xiàn),如基于注解的RequestMappingHandlerMapping
。
來看一個(gè)簡(jiǎn)單的getHandler
方法的實(shí)現(xiàn):
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { return this.handlerMappings.getHandler(request); }
這里,handlerMappings
是一個(gè)List<HandlerMapping>
,Spring會(huì)按照順序遍歷這些HandlerMapping
,直到找到匹配的處理器。
RequestMappingHandlerMapping
的一個(gè)核心方法是getHandlerInternal
:
@Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 基于請(qǐng)求URL和HTTP方法,查找匹配的HandlerMethod // 返回一個(gè)HandlerMethod對(duì)象,包含控制器實(shí)例和方法信息 }
5.3 HandlerAdapter的適配邏輯
HandlerAdapter
負(fù)責(zé)調(diào)用具體的處理器。Spring MVC提供了多種HandlerAdapter
實(shí)現(xiàn),如RequestMappingHandlerAdapter
。
看看getHandlerAdapter
和handle
方法:
protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handlerAdapter.handle(request, response, handler); }
handlerAdapter
是一個(gè)HandlerAdapter
接口的實(shí)現(xiàn),通過supports
方法確定是否適配,然后調(diào)用handle
方法執(zhí)行處理器。
RequestMappingHandlerAdapter
的handle
方法示例:
@Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 調(diào)用Controller方法,處理請(qǐng)求 // 綁定請(qǐng)求參數(shù),執(zhí)行數(shù)據(jù)綁定和驗(yàn)證 // 返回ModelAndView對(duì)象 }
5.4 HandlerInterceptor的執(zhí)行流程
在DispatcherServlet
的doDispatch
方法中,我們看到了攔截器的調(diào)用流程。下面深入看看這些攔截器是如何執(zhí)行的。
假設(shè)我們有一個(gè)HandlerExecutionChain
對(duì)象,它包含了處理器和一組攔截器:
public class HandlerExecutionChain { private final Object handler; private final List<HandlerInterceptor> interceptors; // Getters and constructors }
在applyPreHandle
方法中,DispatcherServlet
會(huì)按照順序調(diào)用每個(gè)攔截器的preHandle
方法:
protected boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler) throws Exception { HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); if(!ObjectUtils.isEmpty(interceptors)) { for (HandlerInterceptor interceptor : interceptors) { if (!interceptor.preHandle(request, response, mappedHandler.getHandler())) { triggerAfterCompletion(request, response, mappedHandler, null, interceptors); return false; } } } return true; }
類似地,在applyPostHandle
和triggerAfterCompletion
方法中,攔截器的postHandle
和afterCompletion
方法依次被調(diào)用,且順序與preHandle
相反。
6. 自定義攔截器的實(shí)現(xiàn)
了解了攔截鏈的基礎(chǔ)后,我們來看看如何在Spring中自定義攔截器。下面是一個(gè)簡(jiǎn)單的自定義攔截器示例:
public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 請(qǐng)求處理前執(zhí)行 System.out.println("Pre Handle method is Calling"); return true; // 返回true繼續(xù)流程,返回false則中斷 } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 請(qǐng)求處理后但視圖渲染前執(zhí)行 System.out.println("Post Handle method is Calling"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 視圖渲染后執(zhí)行 System.out.println("Request and Response is completed"); } }
注冊(cè)這個(gè)攔截器可以通過Java配置或XML配置。在Spring Boot中,可以通過實(shí)現(xiàn)WebMvcConfigurer
接口來進(jìn)行配置:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor()) .addPathPatterns("/**") // 攔截所有請(qǐng)求 .excludePathPatterns("/login"); // 排除特定路徑 } }
通過這樣的配置,MyInterceptor
就會(huì)攔截所有請(qǐng)求,除了/login
路徑。
6.1 攔截器的執(zhí)行順序
如果定義了多個(gè)攔截器,Spring會(huì)按照注冊(cè)的順序執(zhí)行preHandle
方法,而執(zhí)行postHandle
和afterCompletion
則是逆序的。例如:
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new FirstInterceptor()); registry.addInterceptor(new SecondInterceptor()); }
執(zhí)行順序:
FirstInterceptor.preHandle
SecondInterceptor.preHandle
SecondInterceptor.postHandle
FirstInterceptor.postHandle
SecondInterceptor.afterCompletion
FirstInterceptor.afterCompletion
這種設(shè)計(jì)允許后注冊(cè)的攔截器優(yōu)先執(zhí)行后置邏輯和完成邏輯。
7. 總結(jié)
本文,我們深入探討了 Spring攔截鏈的實(shí)現(xiàn)原理和源碼分析,從核心組件如DispatcherServlet
、HandlerMapping
、HandlerAdapter
和HandlerInterceptor
的功能,到攔截鏈的工作流程,再到實(shí)際的源碼分析和自定義攔截器的實(shí)現(xiàn)。
理解這些原理,不僅可以幫助我們更好地使用 Spring提供的攔截器功能,還能讓我們?cè)谛枰獣r(shí)自定義復(fù)雜的攔截邏輯,增強(qiáng)應(yīng)用的靈活性和可維護(hù)性。
到此這篇關(guān)于一文詳解Spring攔截鏈的實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)Spring攔截鏈內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot實(shí)現(xiàn)郵箱發(fā)送(激活碼)功能的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用springboot實(shí)現(xiàn)郵箱發(fā)送(激活碼)功能,文中的示例代碼簡(jiǎn)潔易懂,有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-10-10java實(shí)用小技巧之判斷l(xiāng)ist是否有重復(fù)項(xiàng)簡(jiǎn)單例子
這篇文章主要給大家介紹了關(guān)于java實(shí)用小技巧之判斷l(xiāng)ist是否有重復(fù)項(xiàng)的相關(guān)資料,在開發(fā)工作中我們有時(shí)需要去判斷List集合中是否含有重復(fù)的元素,需要的朋友可以參考下2023-10-10JavaMap兩種遍歷方式keySet與entrySet詳解
這篇文章主要介紹了JavaMap兩種遍歷方式keySet與entrySet,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-03-03Java報(bào)錯(cuò)net.dean.jraw.http.NetworkException異常的原因及解決方法
在開發(fā)涉及網(wǎng)絡(luò)通信的Java應(yīng)用程序時(shí),我們經(jīng)常需要處理各種網(wǎng)絡(luò)異常,net.dean.jraw.http.NetworkException是在使用jRAW庫(kù)時(shí)可能遇到的一個(gè)異常,本文將詳細(xì)探討NetworkException的成因,并提供多種解決方案,需要的朋友可以參考下2024-12-12servlet監(jiān)聽器的學(xué)習(xí)使用(三)
這篇文章主要為大家詳細(xì)介紹了servlet監(jiān)聽器學(xué)習(xí)使用的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09jasypt對(duì)配置文件的數(shù)據(jù)加密與解密方式
這篇文章主要介紹了jasypt對(duì)配置文件的數(shù)據(jù)加密與解密方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Java實(shí)現(xiàn)FIFO任務(wù)調(diào)度隊(duì)列策略
在工作中,很多高并發(fā)的場(chǎng)景中,我們會(huì)用到隊(duì)列來實(shí)現(xiàn)大量的任務(wù)請(qǐng)求。當(dāng)任務(wù)需要某些特殊資源的時(shí)候,我們還需要合理的分配資源,讓隊(duì)列中的任務(wù)高效且有序完成任務(wù)。本文將為大家介紹通過java實(shí)現(xiàn)FIFO任務(wù)調(diào)度,需要的可以參考一下2021-12-12