餓了么急招崗位:說說 Spring 攔截鏈的原理!
這兩天,有小伙伴面了餓了么后端急招崗位,題目:說說 Spring攔截鏈的工作原理?結(jié)果他急眼了...
這篇文章,我們幫這個小伙伴扳回一局,從概念到原理,再到核心源碼,里里外外把 Spring攔截鏈的原理講個透,來,開干!
1. 什么是攔截鏈?
簡單來說,攔截鏈是一個處理請求的攔截器列表,按照一定的順序,一個一個地攔截并處理請求。每個攔截器都可以在請求處理前、處理后或完成后執(zhí)行一些邏輯。
比如,我們可能需要在所有請求處理前進行權(quán)限驗證,在處理后記錄日志,或者在請求完成后釋放資源。這些操作可以通過定義不同的攔截器來實現(xiàn),每個攔截器負責一個特定的任務(wù)。
2. Spring中的攔截鏈
在 Spring MVC中,攔截器鏈是通過HandlerInterceptor接口及其實現(xiàn)類來實現(xiàn)的。Spring的DispatcherServlet作為前端控制器(Front Controller),負責協(xié)調(diào)請求的各個階段,包括調(diào)用攔截器。
攔截器鏈的實現(xiàn)允許多個攔截器按照一定的順序?qū)φ埱筮M行處理。每個攔截器都有機會在請求處理前后執(zhí)行特定的邏輯,這為我們在請求處理流程中插入自定義邏輯提供了極大的靈活性。
3. 攔截鏈的核心組件
要理解攔截鏈的實現(xiàn)原理,首先需要了解 Spring MVC 中 4個核心組件的作用和互相之間的關(guān)系:
- HandlerMapping:HandlerMapping負責將請求URL映射到具體的處理器(Handler)。處理器通常是一個控制器(Controller)的方法。Spring提供了多種HandlerMapping實現(xiàn),如RequestMappingHandlerMapping,支持基于注解的映射。
- HandlerAdapter:HandlerAdapter是負責執(zhí)行具體處理器的組件。它知道如何調(diào)用特定類型的處理器,并返回一個ModelAndView對象,用于渲染視圖。
- DispatcherServlet:DispatcherServlet是Spring MVC的核心組件,充當前端控制器。它接收所有的HTTP請求,協(xié)調(diào)HandlerMapping、HandlerAdapter和視圖解析等組件,最終將請求分發(fā)給合適的處理器進行處理。
- HandlerInterceptor:HandlerInterceptor接口定義了攔截器的基本行為。通過實現(xiàn)該接口,可以在請求處理的不同階段插入自定義邏輯,如請求前、請求后或完成后的處理。
4. 攔截鏈的工作流程
了解了核心組件后,我們接下來分析攔截鏈是如何在這些組件之間協(xié)作,主要流程可以總結(jié)成下面 7個步驟:
- 請求到達 DispatcherServlet:所有的HTTP請求首先由DispatcherServlet接收。
- 查找 Handler:DispatcherServlet使用HandlerMapping查找與請求URL匹配的處理器(Handler)。
- 應(yīng)用攔截器前置:在調(diào)用處理器之前,DispatcherServlet會調(diào)用已注冊的所有攔截器的preHandle方法。這些攔截器按照定義的順序依次執(zhí)行。如果任意一個攔截器的preHandle返回false,請求將被終止,后續(xù)的攔截器和處理器將不會執(zhí)行。
- 調(diào)用 HandlerAdapter 執(zhí)行 Handler:所有前置攔截器的preHandle方法返回true后,DispatcherServlet會調(diào)用HandlerAdapter執(zhí)行具體的處理器方法(如Controller中的方法)。
- 應(yīng)用攔截器后置:處理器執(zhí)行完成后,DispatcherServlet會調(diào)用攔截器的postHandle方法,這些攔截器按照定義的順序逆序執(zhí)行。
- 渲染視圖:DispatcherServlet使用視圖解析器(ViewResolver)渲染最終的視圖,如返回一個HTML頁面。
- 完成攔截器:最后,DispatcherServlet調(diào)用攔截器的afterCompletion方法,通知攔截器請求已經(jīng)完成,同樣按逆序執(zhí)行。
這個流程確保了攔截器可以在請求處理的不同階段插入邏輯,例如驗證、日志記錄、性能監(jiān)控等。
5. 源碼分析
要深入理解攔截鏈的實現(xiàn)原理,我們需要查看Spring MVC的源碼。下面,我們將逐步分析DispatcherServlet、HandlerMapping、HandlerAdapter和HandlerInterceptor等組件的源碼,實現(xiàn)對攔截鏈的全面理解。
(1) DispatcherServlet的角色
DispatcherServlet是整個Spring MVC請求處理流程的中央樞紐。它承擔了接收請求、查找處理器、執(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在處理請求的過程中,依次執(zhí)行了查找處理器、應(yīng)用攔截器的preHandle、調(diào)用處理器、應(yīng)用攔截器的postHandle、渲染視圖以及應(yīng)用攔截器的afterCompletion方法的步驟。
(2) HandlerMapping的查找機制
HandlerMapping用于將請求映射到具體的處理器。Spring MVC提供了多種HandlerMapping實現(xiàn),如基于注解的RequestMappingHandlerMapping。
來看一個簡單的getHandler方法的實現(xiàn):
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
return this.handlerMappings.getHandler(request);
}
這里,handlerMappings是一個List<HandlerMapping>,Spring會按照順序遍歷這些HandlerMapping,直到找到匹配的處理器。
RequestMappingHandlerMapping的一個核心方法是getHandlerInternal:
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 基于請求URL和HTTP方法,查找匹配的HandlerMethod
// 返回一個HandlerMethod對象,包含控制器實例和方法信息
}
(3) HandlerAdapter的適配邏輯
HandlerAdapter負責調(diào)用具體的處理器。Spring MVC提供了多種HandlerAdapter實現(xiàn),如RequestMappingHandlerAdapter。
看看getHandlerAdapter和handle方法:
protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return handlerAdapter.handle(request, response, handler);
}
handlerAdapter是一個HandlerAdapter接口的實現(xiàn),通過supports方法確定是否適配,然后調(diào)用handle方法執(zhí)行處理器。
RequestMappingHandlerAdapter的handle方法示例:
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 調(diào)用Controller方法,處理請求
// 綁定請求參數(shù),執(zhí)行數(shù)據(jù)綁定和驗證
// 返回ModelAndView對象
}
(4) HandlerInterceptor的執(zhí)行流程
在DispatcherServlet的doDispatch方法中,我們看到了攔截器的調(diào)用流程。下面深入看看這些攔截器是如何執(zhí)行的。
假設(shè)我們有一個HandlerExecutionChain對象,它包含了處理器和一組攔截器:
public class HandlerExecutionChain {
private final Object handler;
private final List<HandlerInterceptor> interceptors;
// Getters and constructors
}
在applyPreHandle方法中,DispatcherServlet會按照順序調(diào)用每個攔截器的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. 自定義攔截器的實現(xiàn)
了解了攔截鏈的基礎(chǔ)后,我們來看看如何在Spring中自定義攔截器。下面是一個簡單的自定義攔截器示例:
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 請求處理前執(zhí)行
System.out.println("Pre Handle method is Calling");
returntrue; // 返回true繼續(xù)流程,返回false則中斷
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// 請求處理后但視圖渲染前執(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");
}
}
注冊這個攔截器可以通過Java配置或XML配置。在Spring Boot中,可以通過實現(xiàn)WebMvcConfigurer接口來進行配置:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**") // 攔截所有請求
.excludePathPatterns("/login"); // 排除特定路徑
}
}
通過這樣的配置,MyInterceptor就會攔截所有請求,除了/login路徑。
(1) 攔截器的執(zhí)行順序
如果定義了多個攔截器,Spring會按照注冊的順序執(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è)計允許后注冊的攔截器優(yōu)先執(zhí)行后置邏輯和完成邏輯。
7. 總結(jié)
本文,我們深入探討了 Spring攔截鏈的實現(xiàn)原理和源碼分析,從核心組件如DispatcherServlet、HandlerMapping、HandlerAdapter和HandlerInterceptor的功能,到攔截鏈的工作流程,再到實際的源碼分析和自定義攔截器的實現(xiàn)。
理解這些原理,不僅可以幫助我們更好地使用 Spring提供的攔截器功能,還能讓我們在需要時自定義復(fù)雜的攔截邏輯,增強應(yīng)用的靈活性和可維護性。