springboot-拦截器的实现、执行时机及原理
目錄
一、登錄攔截器使用
1.編寫一個攔截器實現HandlerInterceptor接口
2、攔截器注冊到容器中(實現WebMvcConfigurer的addInterceptors)
3、指定攔截規則【如果是攔截所有,靜態資源也會被攔截】
二、攔截器的實現原理
1.springmvc處理邏輯參考博文
2.DispatcherServlet的doDispatch方法
3.執行攔截器的preHandle方法
4.倒敘執行所有攔截器的postHandle方法
5.頁面成功渲染完成以后觸發 afterCompletion
6.總結
一、登錄攔截器使用
1.編寫一個攔截器實現HandlerInterceptor接口
import lombok.extern.slf4j.Slf4j; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession;/** * 登錄檢查攔截器 * 1、配置好攔截器要攔截哪些請求 * 2、把這些配置放在容器中 */ @Slf4j public class LoginInterceptor implements HandlerInterceptor {/*** 目標方法執行之前執行*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String requestURI = request.getRequestURI();log.info("preHandle攔截的請求路徑是{}",requestURI);//登錄檢查邏輯HttpSession session = request.getSession();Object loginUser = session.getAttribute("loginUser");if(loginUser != null){//返回true就是 放行return true;}//攔截住。未登錄。跳轉到登錄頁request.setAttribute("msg","請先登錄");//response.sendRedirect("/"); // 重定向(重定向無法傳遞數據)request.getRequestDispatcher("/").forward(request,response);// 轉發可以傳遞參數return false;}/*** 目標方法執行完成以后,還未到頁面渲染*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("postHandle執行{}",modelAndView);}/*** 頁面渲染以后執行,或者方法執行失敗執行*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("afterCompletion執行異常{}",ex);} }2、攔截器注冊到容器中(實現WebMvcConfigurer的addInterceptors)
@Configuration public class AdminWebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") //所有請求都被攔截包括靜態資源.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的請求} }3、指定攔截規則【如果是攔截所有,靜態資源也會被攔截】
如第2步,加上攔截的資源和放行的請求。
二、攔截器的實現原理
1.springmvc處理邏輯參考博文
springBoot-springMVC請求處理原理_私人博客,有需要請聯系17854238061(vx同號)-CSDN博客
springmvc響應json與xml原理-詳解數據響應與內容協商:
springboot-springmvc響應json與xml原理-詳解數據響應與內容協商(長文預警,收藏慢啃)_私人博客,有需要請聯系17854238061(vx同號)-CSDN博客
2.DispatcherServlet的doDispatch方法
DispatcherServlet的doDispatch方法是請求的入口。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;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);// Determine handler for the current request.// 拿到請求處理器,里面有攔截器的數組HandlerInterceptor[]// 根據當前請求,找到HandlerExecutionChain【可以處理請求的handler以及handler的所有 攔截器】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 = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}// 目標方法執行之前,會執行該方法// 遍歷所有攔截器,執行攔截器的preHandle方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {// 如果任何一個攔截器返回false。直接跳出不執行目標方法。return;}// 執行目標方法// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);// 倒敘執行所有攔截器的postHandle方法mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}// 該方法是頁面渲染邏輯,頁面成功渲染完成以后,也會倒序觸發 afterCompletionprocessDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {// 前面的步驟有任何異常也都會直接倒序觸發 afterCompletion// 一切正常也會觸發afterCompletiontriggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {// 前面的步驟有任何異常也都會直接倒序觸發 afterCompletion// 一切正常也會觸發afterCompletiontriggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}} }請求處理器,里面有攔截器的數組HandlerInterceptor[]
?
3.執行攔截器的preHandle方法
先來順序執行 所有攔截器的 preHandle方法
? ? ①?如果當前攔截器prehandler返回為true。則執行下一個攔截器的preHandle方法。
? ? ②?如果當前攔截器返回為false。直接 倒序執行所有已經執行了的攔截器的 afterCompletion方法。
如果任何一個攔截器返回false。直接跳出不執行目標方法。
// org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = 0; i < interceptors.length; i++) {HandlerInterceptor interceptor = interceptors[i];if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}}return true; } // org.springframework.web.servlet.HandlerExecutionChain#triggerAfterCompletion void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];try {interceptor.afterCompletion(request, response, this.handler, ex);}catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}} }4.倒敘執行所有攔截器的postHandle方法
// org.springframework.web.servlet.HandlerExecutionChain#applyPostHandle void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = interceptors.length - 1; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];interceptor.postHandle(request, response, this.handler, mv);}} }5.頁面成功渲染完成以后觸發 afterCompletion
// org.springframework.web.servlet.DispatcherServlet#processDispatchResult private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;if (exception != null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}// Did the handler return a view to render?if (mv != null && !mv.wasCleared()) {render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isTraceEnabled()) {logger.trace("No view rendering, null ModelAndView returned.");}}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Concurrent handling started during a forwardreturn;}if (mappedHandler != null) {// Exception (if any) is already handled..// 頁面成功渲染完成以后觸發 afterCompletionmappedHandler.triggerAfterCompletion(request, response, null);} }頁面成功渲染完成以后,也會倒序觸發 afterCompletion
// org.springframework.web.servlet.HandlerExecutionChain#triggerAfterCompletion void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];try {interceptor.afterCompletion(request, response, this.handler, ex);}catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}} }6.總結
1、根據當前請求,找到HandlerExecutionChain【可以處理請求的handler以及handler的所有 攔截器】
2、先來順序執行 所有攔截器的 preHandle方法
(1)、如果當前攔截器prehandler返回為true。則執行下一個攔截器的preHandle
(2)、如果當前攔截器返回為false。直接 倒序執行所有已經執行了的攔截器的 afterCompletion;
3、如果任何一個攔截器返回false。直接跳出不執行目標方法
4、所有攔截器都返回True。則繼續執行目標方法
5、目標方法執行完成之后,倒序執行所有攔截器的postHandle方法。
6、前面的步驟有任何異常都會直接倒序觸發 afterCompletion
7、頁面成功渲染完成以后,也會倒序觸發 afterCompletion
總結
以上是生活随笔為你收集整理的springboot-拦截器的实现、执行时机及原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springboot-springmvc
- 下一篇: springboot-嵌入式Servle