在 Spring Boot 中,攔截器和動態(tài)代理都是用來實現(xiàn)功能增強的,所以在很多時候,有人會認為攔截器的底層是通過動態(tài)代理實現(xiàn)的,所以本文就來盤點一下他們兩的區(qū)別,以及攔截器的底層實現(xiàn)。
攔截器(Interceptor)準確來說在 Spring MVC 中的一個很重要的組件,用于攔截 Controller 的請求。它的主要作用有以下幾個:
典型的使用場景是身份認證、授權(quán)檢查、請求日志記錄等。
在 Spring Boot 中攔截器的實現(xiàn)分為兩步:
具體實現(xiàn)如下:
① 實現(xiàn)自定義攔截器
import org.springframework.stereotype.Component;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@Componentpublic class TestInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("攔截器:執(zhí)行 preHandle 方法。"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("攔截器:執(zhí)行 postHandle 方法。"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("攔截器:執(zhí)行 afterCompletion 方法。"); }}
其中:
② 配置攔截規(guī)則
然后,我們再將上面的攔截器注入到項目配置文件中,并設(shè)置相應(yīng)攔截規(guī)則,具體實現(xiàn)代碼如下:
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class AppConfig implements WebMvcConfigurer { // 注入攔截器 @Autowired private TestInterceptor testInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(testInterceptor) // 添加攔截器 .addPathPatterns("/**"); // 攔截所有地址 .excludePathPatterns("/login"); // 放行接口 }}
這樣我們的攔截器就實現(xiàn)完了。
Spring Boot 攔截器是基于 Java 的 Servlet 規(guī)范實現(xiàn)的,通過實現(xiàn) HandlerInterceptor 接口來實現(xiàn)攔截器功能。
在 Spring Boot 框架的執(zhí)行流程中,攔截器被注冊在 DispatcherServlet 的 doDispatch() 方法中,該方法是 Spring Boot 框架的核心方法,用于處理請求和響應(yīng)。
程序每次執(zhí)行時都會調(diào)用 doDispatch() 方法時,并驗證攔截器(鏈),之后再根據(jù)攔截器返回的結(jié)果,進行下一步的處理。如果返回的是 true,那么繼續(xù)調(diào)用目標(biāo)方法,反之則會直接返回驗證失敗給前端。
doDispatch 源碼實現(xiàn)如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { try { ModelAndView mv = null; Object dispatchException = null; try { processedRequest = this.checkMultipart(request); multipartRequestParsed = processedRequest != request; mappedHandler = this.getHandler(processedRequest); if (mappedHandler == null) { this.noHandlerFound(processedRequest, response); return; } HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); 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; } } // 調(diào)用預(yù)處理【重點】 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 執(zhí)行 Controller 中的業(yè)務(wù) mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } this.applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception var20) { dispatchException = var20; } catch (Throwable var21) { dispatchException = new NestedServletException("Handler dispatch failed", var21); } this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException); } catch (Exception var22) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22); } catch (Throwable var23) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23)); } } finally { if (asyncManager.isConcurrentHandlingStarted()) { if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else if (multipartRequestParsed) { this.cleanupMultipart(processedRequest); } }}
從上述源碼可以看出在開始執(zhí)行 Controller 之前,會先調(diào)用 預(yù)處理方法 applyPreHandle,而 applyPreHandle 方法的實現(xiàn)源碼如下:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) { // 獲取項目中使用的攔截器 HandlerInterceptor HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i); if (!interceptor.preHandle(request, response, this.handler)) { this.triggerAfterCompletion(request, response, (Exception)null); return false; } } return true;}
從上述源碼可以看出,在 applyPreHandle 中會獲取所有的攔截器 HandlerInterceptor 并執(zhí)行攔截器中的 preHandle 方法,這樣就會咱們前面定義的攔截器對應(yīng)上了,如下圖所示:
此時用戶登錄權(quán)限的驗證方法就會執(zhí)行,這就是攔截器的執(zhí)行過程。因此,可以得出結(jié)論,攔截器的實現(xiàn)主要是依賴 Servlet 或 Spring 執(zhí)行流程來進行攔截和功能增強的。
動態(tài)代理是一種設(shè)計模式,它是指在運行時提供代理對象,來擴展目標(biāo)對象的功能。在 Spring 中的,動態(tài)代理的實現(xiàn)手段有以下兩種:
動態(tài)代理的主要作用包括:
因此,我們可以得出結(jié)論,攔截器和動態(tài)代理雖然都是用來實現(xiàn)功能增強的,但二者完全不同,他們的主要區(qū)別體現(xiàn)在以下幾點:
在 Spring Boot 中,攔截器和動態(tài)代理都是用來實現(xiàn)功能增強的,但二者沒有任何關(guān)聯(lián)關(guān)系,它的區(qū)別主要體現(xiàn)在使用范圍、實現(xiàn)原理、加入時機和使用的難易程度都是不同的
本文鏈接:http://www.tebozhan.com/showinfo-26-10448-0.htmlSpringBoot攔截器和動態(tài)代理有什么區(qū)別?
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: WPF依賴屬性介紹和用法舉例