Springboot項目快速實現(xiàn)攔截器功能
前言
上一篇文章分享了Springboot項目快速實現(xiàn)過濾器功能,本篇文章接著來盤一盤攔截器,仔細研究后會發(fā)現(xiàn),其實攔截器和過濾器的功能非常類似,可以理解為面向切面編程的一種具體實現(xiàn)。下面就其功能特性、工作原理、涉及到的核心類以及具體的實現(xiàn)方式幾個方面進行梳理,以便在實際業(yè)務開發(fā)過程中,可以根據(jù)實際需要選擇合適的實現(xiàn)訪求。
環(huán)境配置
jdk版本:1.8
開發(fā)工具:Intellij iDEA 2020.1
springboot:2.3.9.RELEASE
HandleInterceptor介紹
攔截器,在java中只是一種概念,并沒有一個具體的實現(xiàn)或標準,通常所說的java web的攔截器實際是指HandleInterceptor接口,這是Spring MVC提供的一套攔截機制,可以在controller處理請求之后和響應處理結果之后,對請求信息和響應結果進行攔截;但不能修改具體的請求信息和響應結果;Spring MVC的攔截器的概念和servlet的Filter非常類似,但是在執(zhí)行順序上是有所不同的,下面會重點介紹;從本質上來說,SpringMVC的攔截器機制,是AOP(面向切面編程)的一種具體實現(xiàn),可以很方面用戶對實際業(yè)務中公共的一些業(yè)務進行橫向抽取,但是和servlet的Filter一樣,有一定的局限性,如能對請求信息和響應結果進行攔截,但不能修改具體的請求信息和響應結果;能攔截controller層的方法,但是不能攔截service層的方法;
工作原理
如果把過濾器和攔截器放在一起來分析其工作原理,就需要再次明確一件事:Filter接口的全限定類名是javax.servlet.Filter,HandleInterceptor接口的全限定類名是org.springframework.web.servlet.HandlerInterceptor,從這里就可以看得出來,F(xiàn)ilter是servlet里就有的接口,HandleInterceptor是Spring中新增的接口,兩個是來源完全不同的東西,但功能卻很類似,那么放在一起又會發(fā)生什么奇妙的事呢?
1、過濾器1、過濾器2、攔截1、攔截2對象,會在Spring容器啟動的過程中,完成bean的注冊;
2、當客戶端向服務端發(fā)起http請求時,在請求到達具體的controller方法之前,會先經(jīng)過過濾器、攔截器的處理,其中過濾器的執(zhí)行時機要早于攔截器;和過濾器一樣,如果當前請求匹配到了多個攔截器,會形成一個攔截器鏈,按照默認或指定的優(yōu)先級,依次經(jīng)過各個攔截器對象的處理之后,才會到達具體的controller方法;
3、到達具體的controller方法后,開始業(yè)務處理;得到業(yè)務處理結果后,響應結果也會經(jīng)過請求進來時的所有過濾器、攔截器的處理,不同的是順序與請求進來時完全相反,即先進后出;
實現(xiàn)方式
1、實現(xiàn)org.springframework.web.servlet.HandlerInterceptor接口;
2、繼承 org.springframework.web.servlet.handler.HandlerInterceptorAdapter類;
核心類
HandlerInterceptor
HandlerInterceptor是SpringMVC提供的實現(xiàn)攔截器功能的一個標準接口,接口內(nèi)有三個方法:
1、preHandler(HttpServletRequest request, HttpServletResponse response, Object handler) ,方法在請求處理之前會被調用。該方法在 Interceptor 類中最先執(zhí)行,用來進行一些前置初始化操作或是對當前請求做預處理,也可以進行一些判斷來決定請求是否要繼續(xù)進行下去。該方法的返回值是 Boolean 類型;當它返回 false 時,表示請求結束,后續(xù)的 Interceptor 和 Controller 都不會再執(zhí)行;當它返回為 true 時,會繼續(xù)調用下一個 Interceptor 的 preHandle 方法,如果已經(jīng)是最后一個 Interceptor 的時候就會調用當前請求的 Controller 方法;
2、postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 方法在當前請求處理完成之后,也就是 Controller 方法調用之后執(zhí)行,但是它會在 DispatcherServlet 進行視圖返回渲染之前被調用,所以我們可以在這個方法中對 Controller 處理之后的 ModelAndView 對象進行操作;
3、afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法需要在當前對應的 Interceptor 類的 preHandle 方法返回值為 true 時才會執(zhí)行;該方法將在整個請求結束之后,也就是在 DispatcherServlet 渲染了對應的視圖之后執(zhí)行,主要用來進行資源清理或釋放。
HandlerInterceptorAdapter
HandlerInterceptorAdapter是一個抽象類,該抽象類實現(xiàn)了AsyncHandlerInterceptor接口,而AsyncHandlerInterceptor接口又繼承于HandlerInterceptor,因此可以認為HandlerInterceptorAdapter是實現(xiàn)了HandlerInterceptor接口,但是HandlerInterceptorAdapter是抽象類,實際上并沒有具體實現(xiàn),所以在實現(xiàn)攔截器功能功能的兩種方式本質上是一種;
代碼實現(xiàn)
定義兩個攔截器:MyInterceptor1和MyInterceptor2,通過一次完成的請求,來分析一下HandlerInterceptor接口的preHandle()、postHandle()、afterCompletion()是如何工作的?
1、定義和注冊兩個攔截器:MyInterceptor1和MyInterceptor2,設置攔截器的攔截路徑為/person/get*,myInterceptor2的優(yōu)先級高于myInterceptor1;
2、發(fā)起http請求(URL:/person/get);
3、/person/get請求在執(zhí)行PersonController#getPerson()之前,會先執(zhí)行到MyInterceptor1和MyInterceptor2的preHandler()方法;
4、如果MyInterceptor1和MyInterceptor2的preHandler()方法返回都為true,則會執(zhí)行到PersonController#getPerson();
5、PersonController#getPerson()執(zhí)行完成后,還沒有返回視圖渲染對象之前MyInterceptor1和MyInterceptor2的postHandle()方法觸發(fā)執(zhí)行;
6、再然后就是整個請求處理完之后,MyInterceptor1和MyInterceptor2的afterCompletion()方法觸發(fā)執(zhí)行;
PersonController.java
@Controller @RequestMapping("/person") @Slf4j public class PersonController { @Autowired private IPersonService personService; @GetMapping("/get") @ResponseBody public Person getPerson(Integer id) { Person person = this.personService.get(id); log.info("http://查詢person詳情執(zhí)行完成"); return person; } }
定義WebConfig類實現(xiàn)WebMvcConfigurer接口,實現(xiàn)addInterceptors(),完成攔截器的注冊以及設置好攔截路徑和優(yōu)先級順序;
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor2()) .addPathPatterns("/person/get*") .order(1); registry.addInterceptor(new MyInterceptor1()) .addPathPatterns("/person/get*")//設置攔截請求路徑;*是通配符; .order(2)//設置攔截器對象的優(yōu)先級,如果有多個攔截器對象,設置數(shù)字越小,優(yōu)先級越高; .excludePathPatterns("/test");//設置排除攔截的請求路徑; } }
定義MyInterceptor1類實現(xiàn)HandlerInterceptor接口
@Slf4j public class MyInterceptor1 implements HandlerInterceptor { //preHandle方法在請求處理之前被調用;當返回true,則表示可以繼續(xù)后續(xù)請求處理;當返回false,則表示請求結束; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("http://myInterceptor1的preHandle方法開始執(zhí)行"); log.info("http://請求路徑:{}",request.getRequestURI()); HandlerMethod handlerMethod = (HandlerMethod) handler; log.info("http://攔截類:{},方法:{}",handlerMethod.getBean().getClass().getName(),handlerMethod.getMethod().getName()); Enumeration<String> parameterNames = request.getParameterNames(); while (parameterNames.hasMoreElements()){ String parameterName = parameterNames.nextElement(); String parameterValue = request.getParameter(parameterName); log.info("http://請求參數(shù)>{}:{}",parameterName,parameterValue); } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("http://myInterceptor1的postHandle方法開始執(zhí)行"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("http://myInterceptor1的afterCompletion方法開始執(zhí)行"); } }
定義MyInterceptor2,繼承HandlerInterceptorAdapter抽象類,重寫HandlerInterceptorAdapter類的方法;
@Slf4j public class MyInterceptor2 extends HandlerInterceptorAdapter { //preHandle方法在請求處理之前被調用;當返回true,則表示可以繼續(xù)后續(xù)請求處理;當返回false,則表示請求結束; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("http://myInterceptor1的preHandle方法開始執(zhí)行"); log.info("http://請求路徑:{}",request.getRequestURI()); HandlerMethod handlerMethod = (HandlerMethod) handler; log.info("http://攔截類:{},方法:{}",handlerMethod.getBean().getClass().getName(),handlerMethod.getMethod().getName()); Enumeration<String> parameterNames = request.getParameterNames(); while (parameterNames.hasMoreElements()){ String parameterName = parameterNames.nextElement(); String parameterValue = request.getParameter(parameterName); log.info("http://請求參數(shù)>{}:{}",parameterName,parameterValue); } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("http://myInterceptor1的postHandle方法開始執(zhí)行"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("http://myInterceptor1的afterCompletion方法開始執(zhí)行"); }
結果驗證
Filter與HandleInterceptor的執(zhí)行順序:在請求處理階段,先經(jīng)過Filter然后再經(jīng)過HandleInterceptor,在響應處理階段,先經(jīng)過HandleInterceptor,再經(jīng)過Filter,即先進后出;
總結
攔截器的功能與Filter比較類似,實現(xiàn)方式也比較簡單,需要特別注意的是攔截器的執(zhí)行時機稍晚于過濾器。那么Spring的AOP與過濾器、攔截器相比,又有哪些不同和需要注意的事項呢?下一篇文章會把這個坑給填上。
到此這篇關于Springboot項目快速實現(xiàn)攔截器功能的文章就介紹到這了,更多相關Springboot實現(xiàn)攔截器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
100-200之間所有素數(shù)求和程序代碼(二個版本)
寫一個求100-200之間素數(shù),并求和的程序,大家參考使用吧2013-11-11Java中的SynchronousQueue阻塞隊列及使用場景解析
這篇文章主要介紹了Java中的SynchronousQueue阻塞隊列及使用場景解析,SynchronousQueue 是 Java 中的一個特殊的阻塞隊列,它的主要特點是它的容量為0,這意味著 SynchronousQueue不會存儲任何元素,需要的朋友可以參考下2023-12-12JavaWeb開發(fā)之JSTL標簽庫的使用、 自定義EL函數(shù)、自定義標簽(帶屬性的、帶標簽體的)
這篇文章主要介紹了JavaWeb開發(fā)之JSTL標簽庫的使用、 自定義EL函數(shù)、自定義標簽(帶屬性的、帶標簽體的),需要的朋友可以參考下2017-02-02Java中的MessageFormat.format用法實例
這篇文章主要介紹了Java中的MessageFormat.format用法實例,本文先是講解了MessageFormat的語法,然后給出了多個操作實例,需要的朋友可以參考下2015-06-06