您好,登錄后才能下訂單哦!
這篇文章主要介紹“Spring Boot中三種攔截器的創建方法”,在日常操作中,相信很多人在Spring Boot中三種攔截器的創建方法問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Spring Boot中三種攔截器的創建方法”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
在web開發中,攔截器是經常用到的功能。它可以幫我們驗證是否登陸、權限認證、數據校驗、預先設置數據以及統計方法的執行效率等等。今天就來詳細的談一下spring中的攔截器。spring中攔截器主要分種,一個是HandlerInterceptor,一個是MethodInterceptor。
HandlerInterceptor是springMVC項目中的攔截器,它攔截的目標是請求的地址,比MethodInterceptor先執行。實現一個HandlerInterceptor攔截器可以直接實現HandlerInterceptor接口,也可以繼承HandlerInterceptorAdapter類。這兩種方法殊途同歸,其實HandlerInterceptorAdapter也就是聲明了HandlerInterceptor接口中所有方法的默認實現,而我們在繼承他之后只需要重寫必要的方法。下面就是HandlerInterceptorAdapter的代碼,可以看到一個方法只是默認返回true,另外兩個是空方法:
/** * 自定義攔截器-基于springmvc * @ClassName: CustomInterceptor * @Description: springMVC項目中的攔截器,它攔截的目標是請求的地址,比MethodInterceptor先執行。 * 該攔截器只能過濾action請求,SPring允許多個攔截器同時存在,通過攔截器鏈管理。 * 當preHandle return true時,執行下一個攔截器,直到所有攔截器執行完,再運行被攔截的請求。 * 當preHandle return false時, 不再執行后續的攔截器鏈及被攔截的請求。 * @author OnlyMate * @Date 2018年8月28日 下午2:30:22 * */ public class CustomInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // TODO Auto-generated method stub return HandlerInterceptor.super.preHandle(request, response, handler); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // TODO Auto-generated method stub HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
這三個方法都是干什么的,有什么作用,什么時候調用,不同的攔截器之間是怎樣的調用順序呢?這還得參考一下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. 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 (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); 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); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
代碼有點長,但是它封裝了springMVC處理請求的整個過程。首先根據請求找到對應的HandlerExecutionChain,它包含了處理請求的handler和所有的HandlerInterceptor攔截器;然后在調用hander之前分別調用每個HandlerInterceptor攔截器的preHandle方法,若有一個攔截器返回false,則會調用triggerAfterCompletion方法,并且立即返回不再往下執行;若所有的攔截器全部返回true并且沒有出現異常,則調用handler返回ModelAndView對象;再然后分別調用每個攔截器的postHandle方法;最后,即使是之前的步驟拋出了異常,也會執行triggerAfterCompletion方法。關于攔截器的處理到此為止,接下來看看triggerAfterCompletion做了什么
private void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, Exception ex) throws Exception { if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, ex); } throw ex; }
根據以上的代碼,分析一下不同攔截器及其方法的執行順序。假設有5個攔截器編號分別為12345,若一切正常則方法的執行順序是12345的preHandle,54321的postHandle,54321的afterCompletion。若編號3的攔截器的preHandle方法返回false或者拋出了異常,接下來會執行的是21的afterCompletion方法。這里要注意的地方是,我們在寫一個攔截器的時候要謹慎的處理preHandle中的異常,因為這里一旦有異常拋出就不會再受到這個攔截器的控制。12345的preHandle的方法執行過之后,若handler出現了異常或者某個攔截器的postHandle方法出現了異常,則接下來都會執行54321的afterCompletion方法,因為只要12345的preHandle方法執行完,當前攔截器的攔截器就會記錄成編號5的攔截器,而afterCompletion總是從當前的攔截器逆向的向前執行。 另外,實現HandlerInterceptor攔截器還有一個方法,就是實現WebRequestInterceptor接口。其實它和剛才的兩種方法也是殊途同歸,最終還是被spring適配成HandlerInterceptor。有一點不同,它的preHandle方法最終只會返回true。
這里可以根據自己的需求在對應方法中寫自己業務處理邏輯
/** * 自定義攔截器-基于springmvc * @ClassName: CustomInterceptor * @Description: springMVC項目中的攔截器,它攔截的目標是請求的地址,比MethodInterceptor先執行。 * 該攔截器只能過濾action請求,SPring允許多個攔截器同時存在,通過攔截器鏈管理。 * 當preHandle return true時,執行下一個攔截器,直到所有攔截器執行完,再運行被攔截的請求。 * 當preHandle return false時, 不再執行后續的攔截器鏈及被攔截的請求。 * @author OnlyMate * @Date 2018年8月28日 下午2:30:22 * */ public class CustomInterceptor implements HandlerInterceptor { private Logger logger = LoggerFactory.getLogger(CustomInterceptor.class); /** * 在請求處理之前執行,主要用于權限驗證、參數過濾等 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { logger.info("CustomInterceptor ==> preHandle method: do request before"); return true; } /** * 當前請求進行處理之后執行,主要用于日志記錄、權限檢查、性能監控、通用行為等 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { logger.info("CustomInterceptor ==> postHandle method: do request after"); } /** * 當前對應的interceptor的perHandle方法的返回值為true時,postHandle執行完成并渲染頁面后執行,主要用于資源清理工作 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { logger.info("CustomInterceptor ==> afterCompletion method: do request finshed"); } }
配置如下:
/** * Web MVC 配置適配器 * @ClassName: WebAppConfigurer * @Description: * @author OnlyMate * @Date 2018年8月28日 下午2:39:31 * * WebAppConfigurer extends WebMvcConfigurerAdapter 在Spring Boot2.0版本已過時了,用官網說的新的類替換 * */ @Configuration public class WebAppConfigurer implements WebMvcConfigurer { /** * 注入自定義攔截器 * @Title: addInterceptors * @Description: 先add的攔截器會越靠外,即越靠近瀏覽器 * @Date 2018年8月28日 下午2:47:28 * @author OnlyMate * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { WebMvcConfigurer.super.addInterceptors(registry); registry.addInterceptor(new CustomInterceptor()).addPathPatterns("/**");//攔截所有請求 } }
MethodInterceptor是AOP項目中的攔截器,它攔截的目標是方法,即使不是controller中的方法。實現MethodInterceptor攔截器大致也分為兩種,一種是實現MethodInterceptor接口,另一種利用AspectJ的注解或配置。
/** * 自定義攔截器-方法攔截器,基于spring aop * @ClassName: CustomMethodInterceptor * @Description: AOP項目中的攔截器,它攔截的目標是方法 * 配置在applicationContext.xml中 * @author OnlyMate * @Date 2018年8月29日 下午3:35:24 * */ public class CustomMethodInterceptor implements MethodInterceptor { private Logger logger = LoggerFactory.getLogger(CustomMethodInterceptor.class); @Override public Object invoke(MethodInvocation invocation) throws Throwable { logger.info("CustomMethodInterceptor ==> invoke method: process method name is {}", invocation.getMethod().getName()); //TODO 處理操作 return invocation.proceed(); } }
配置說明
<bean id="customMethodInterceptor" class="com.onlymate.springboot.interceptor.CustomMethodInterceptor" /> <aop:config proxy-target-class="false"> <!-- 方法攔截器,基于spring aop 實現配置 --> <!-- 掃描使用了注解的方法進行攔截 --> <aop:advisor pointcut="@annotation(com.onlymate.springboot.annotation.CustomAnnotation)" advice-ref="customMethodInterceptor" /> <!-- 指定包路徑下的方法 --> <aop:advisor pointcut="execution(* com.onlymate.springboot.controller.*.*(..))" advice-ref="customMethodInterceptor" /> </aop:config>
CustomAnnotation自定義注解
/** * 自定義注解對象 * @ClassName: TableSplit * @Description: TODO * @author OnlyMate * @Date 2018年5月22日 上午11:43:57 * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface CustomAnnotation { /** 需攔截方法名描述 */ String name() default ""; /** 加密 */ String[] encrypt() default {}; /** 解密 */ String[] decrypt() default {}; }
/** * 自定義攔截器-方法攔截器,基于注解的AspectJ方式 * @ClassName: CustomAutoAspectJInterceptor * @Description: 配置在applicationContext.xml中 * @author OnlyMate * @Date 2018年8月29日 下午4:03:49 * */ @Component @Aspect public class CustomAutoAspectJInterceptor { private Logger logger = LoggerFactory.getLogger(CustomAutoAspectJInterceptor.class); @Around("execution (* com.onlymate.springboot.controller.*.*(..))") public Object around(ProceedingJoinPoint point) throws Throwable{ logger.info("CustomAutoAspectJInterceptor ==> invoke method: process method class is {}", point.getTarget().getClass()); //TODO 處理操作 return point.proceed(); } }
/** * 自定義攔截器-方法攔截器,基于AspectJ方式 * @ClassName: CustomAspectJInterceptor * @Description: 配置在applicationContext.xml中 * @author OnlyMate * @Date 2018年8月29日 下午4:03:49 * */ public class CustomAspectJInterceptor { private Logger logger = LoggerFactory.getLogger(CustomAspectJInterceptor.class); public Object around(ProceedingJoinPoint point) throws Throwable{ logger.info("CustomAspectJInterceptor ==> invoke method: process method class is {}", point.getTarget().getClass()); //TODO 處理操作 return point.proceed(); } }
<bean id="customAspectJInterceptor" class="com.onlymate.springboot.interceptor.CustomAspectJInterceptor"/> <aop:config proxy-target-class="false"> <!-- 方法攔截器,基于AspectJ實現方式一 --> <aop:aspect ref="customAspectJInterceptor"> <aop:around method="around" pointcut="execution(* com.onlymate.springboot.controller.*.*(..))"/> </aop:aspect> </aop:config> <!-- 方法攔截器,基于AspectJ實現方式二 --> <!-- 自動掃描使用了aspectj注解的類 --> <aop:aspectj-autoproxy/>
上面的兩種攔截器都能起到攔截的效果,但是他們攔截的目標不一樣,實現的機制不同,所以有的時候適用不同的場景。HandlerInterceptoer攔截的是請求地址,所以針對請求地址做一些驗證、預處理等操作比較合適。當你需要統計請求的響應時間時MethodInterceptor將不太容易做到,因為它可能跨越很多方法或者只涉及到已經定義好的方法中一部分代碼。MethodInterceptor利用的是AOP的實現機制,在本文中只說明了使用方式,關于原理和機制方面介紹的比較少,因為要說清楚這些需要講出AOP的相當一部分內容。在對一些普通的方法上的攔截HandlerInterceptoer就無能為力了,這時候只能利用AOP的MethodInterceptor。利用MethodInterceptor就可以很容易的實現一個日志攔截處理。 另外,還有一個跟攔截器類似的東西----Filter。Filter是Servlet規范規定的,不屬于spring框架,也是用于請求的攔截。但是它適合更粗粒度的攔截,在請求前后做一些編解碼處理、日志記錄等。而攔截器則可以提供更細粒度的,更加靈活的,針對某些請求、某些方法的組合的解決方案。
到此,關于“Spring Boot中三種攔截器的創建方法”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。