<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在web開發中,攔截器是經常用到的功能。它可以幫我們預先設定資料以及統計方法的執行效率等等。
今天就來詳細的談一下spring中的攔截器。spring中攔截器主要分兩種,一個是HandlerInterceptor,一個是MethodInterceptor。
HandlerInterceptor是springMVC專案中的攔截器,它攔截的目標是請求的地址,比MethodInterceptor先執行。其工作原理是當請求來時先進性預處理,如下。
這裡我們可以實現一個通過HandlerInterceptor實現列印請求開始和結束的紀錄檔,如下。
1.依賴引入
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
2.實現類
攔截器類
@Component public class EasyLogControllerInterceptor implements HandlerInterceptor { /** * 在controller呼叫之前執行 */ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(request.getRequestURI()+"開始執行"); return true; } /** * 在controller呼叫中執行 */ public void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } /** * 在controller呼叫後執行 */ public void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println(request.getRequestURI()+"執行結束"); } }
controller類
@RestController public class TestController { @GetMapping("/hello") public Map<String,String> hello(){ Map<String,String> response=new HashMap<>(); response.put("msg","hello"); return response; } }
設定類
@Configuration public class IntercepterConfig implements WebMvcConfigurer { @Autowired private EasyLogControllerInterceptor easyLogControllerInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { //addPathPatterns用於新增攔截路徑 //excludePathPatterns用於新增不攔截的路徑 registry.addInterceptor(easyLogControllerInterceptor).addPathPatterns("/hello"); } //此方法用於設定靜態資源路徑 @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("classpath:/my/"); } }
3.執行效果
1.1.1HandlerInterceptor講解
實現一個HandlerInterceptor攔截器可以直接實現HandlerInterceptor介面,也可以繼承HandlerInterceptorAdapter類。這兩種方法殊途同歸,其實HandlerInterceptorAdapter也就是宣告了HandlerInterceptor介面中所有方法的預設實現,而我們在繼承他之後只需要重寫必要的方法。
下面就是HandlerInterceptorAdapter的程式碼,可以看到一個方法只是預設返回true,另外兩個是空方法:
public abstract class HandlerInterceptorAdapter implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } public void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } public void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
這三個方法都是幹什麼的,有什麼作用,什麼時候呼叫,不同的攔截器之間是怎樣的呼叫順序呢?
先補一張圖:
這還得參考一下DispatcherServlet的doDispatch方法:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; int interceptorIndex = -1; try { ModelAndView mv; boolean errorView = false; try { processedRequest = checkMultipart(request); // Determine handler for the current request. mappedHandler = getHandler(processedRequest, false); if (mappedHandler == null || mappedHandler.getHandler() == 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 (logger.isDebugEnabled()) { String requestUri = urlPathHelper.getRequestUri(request); logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // Apply preHandle methods of registered interceptors. HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); if (interceptors != null) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) { triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); return; } interceptorIndex = i; } } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // Do we need view name translation? if (mv != null && !mv.hasView()) { mv.setViewName(getDefaultViewName(request)); } // Apply postHandle methods of registered interceptors. if (interceptors != null) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv); } } } catch (ModelAndViewDefiningException ex) { logger.debug("ModelAndViewDefiningException encountered", ex); mv = ex.getModelAndView(); } catch (Exception ex) { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(processedRequest, response, handler, ex); errorView = (mv != null); } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { render(mv, processedRequest, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isDebugEnabled()) { logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "': assuming HandlerAdapter completed request handling"); } } // Trigger after-completion for successful outcome. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); } catch (Exception ex) { // Trigger after-completion for thrown exception. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); throw ex; } catch (Error err) { ServletException ex = new NestedServletException("Handler processing failed", err); // Trigger after-completion for thrown exception. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); throw ex; } finally { // Clean up any resources used by a multipart request. if (processedRequest != request) { cleanupMultipart(processedRequest); } } }
程式碼有點長,但是它封裝了springMVC處理請求的整個過程。首先根據請求找到對應的HandlerExecutionChain,它包含了處理請求的handler和所有的HandlerInterceptor攔截器;然後在呼叫hander之前分別呼叫每個HandlerInterceptor攔截器的preHandle方法,若有一個攔截器返回false,則會呼叫triggerAfterCompletion方法,並且立即返回不再往下執行;若所有的攔截器全部返回true並且沒有出現異常,則呼叫handler返回ModelAndView物件;再然後分別呼叫每個攔截器的postHandle方法;最後,即使是之前的步驟丟擲了異常,也會執行triggerAfterCompletion方法。
MethodInterceptor是AOP專案中的攔截器,它攔截的目標是方法,即使不是controller中的方法。具體使用方式可以參考SpringBoot中利用AOP和攔截器實現自定義註解
上面的兩種攔截器都能起到攔截的效果,但是他們攔截的目標不一樣,實現的機制不同,所以有的時候適用不同的場景。
HandlerInterceptoer攔截的是請求地址,所以針對請求地址做一些驗證、預處理等操作比較合適。當你需要統計請求的響應時間時MethodInterceptor將不太容易做到,因為它可能跨越很多方法或者只涉及到已經定義好的方法中一部分程式碼。MethodInterceptor利用的是AOP的實現機制,在本文中只說明瞭使用方式,關於原理和機制方面介紹的比較少,因為要說清楚這些需要講出AOP的相當一部分內容。在對一些普通的方法上的攔截HandlerInterceptoer就無能為力了,這時候只能利用AOP的MethodInterceptor。
另外,還有一個跟攔截器類似的東西----Filter。Filter是Servlet規範規定的,不屬於spring框架,也是用於請求的攔截。但是它適合更粗粒度的攔截,在請求前後做一些編解碼處理、紀錄檔記錄等。而攔截器則可以提供更細粒度的,更加靈活的,針對某些請求、某些方法的組合的解決方案。
另外的另外,用過人人網的ROSE框架的人都會非常喜歡它的攔截器功能。因為它實現了全註解的方式,只要在類的名字上加上攔截器的註解即表示這是一個攔截器。而使用這個攔截器的方法或者controller也只需在方法或controller的上面加上這個攔截器的註解。其實這是一個關注點的轉變,spring的切面控制在組態檔中,組態檔關注哪些地方需要攔截。而在ROSE中,則是在需要攔截的地方關注我要被誰攔截。
以上就是一文了解Spring中攔截器的原理與使用的詳細內容,更多關於Spring攔截器的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45