<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Springboot 中使用攔截器

          共 9542字,需瀏覽 20分鐘

           ·

          2021-04-23 08:30

          ????關(guān)注后回復(fù) “進(jìn)群” ,拉你進(jìn)程序員交流群????
          作者丨武哥
          來(lái)源丨武哥聊編程

          攔截器的原理很簡(jiǎn)單,是 AOP 的一種實(shí)現(xiàn),專門攔截對(duì)動(dòng)態(tài)資源的后臺(tái)請(qǐng)求,即攔截對(duì)控制層的請(qǐng)求。使用場(chǎng)景比較多的是判斷用戶是否有權(quán)限請(qǐng)求后臺(tái),更拔高一層的使用場(chǎng)景也有,比如攔截器可以結(jié)合 websocket 一起使用,用來(lái)攔截 websocket 請(qǐng)求,然后做相應(yīng)的處理等等。攔截器不會(huì)攔截靜態(tài)資源,Spring Boot 的默認(rèn)靜態(tài)目錄為 resources/static,該目錄下的靜態(tài)頁(yè)面、js、css、圖片等等,不會(huì)被攔截(也要看如何實(shí)現(xiàn),有些情況也會(huì)攔截,我在下文會(huì)指出)。

          1. 攔截器的快速使用

          使用攔截器很簡(jiǎn)單,只需要兩步即可:定義攔截器和配置攔截器。在配置攔截器中,Spring Boot 2.0 以后的版本和之前的版本有所不同,我會(huì)重點(diǎn)講解一下這里可能出現(xiàn)的坑。

          1.1 定義攔截器

          定義攔截器,只需要實(shí)現(xiàn) HandlerInterceptor 接口,HandlerInterceptor 接口是所有自定義攔截器或者 Spring Boot 提供的攔截器的鼻祖,所以,首先來(lái)了解下該接口。該接口中有三個(gè)方法: preHandle(……)postHandle(……) 和 afterCompletion(……) 。

          preHandle(……) 方法:該方法的執(zhí)行時(shí)機(jī)是,當(dāng)某個(gè) url 已經(jīng)匹配到對(duì)應(yīng)的 Controller 中的某個(gè)方法,且在這個(gè)方法執(zhí)行之前。所以 preHandle(……) 方法可以決定是否將請(qǐng)求放行,這是通過(guò)返回值來(lái)決定的,返回 true 則放行,返回 false 則不會(huì)向后執(zhí)行。
          postHandle(……) 方法:該方法的執(zhí)行時(shí)機(jī)是,當(dāng)某個(gè) url 已經(jīng)匹配到對(duì)應(yīng)的 Controller 中的某個(gè)方法,且在執(zhí)行完了該方法,但是在 DispatcherServlet 視圖渲染之前。所以在這個(gè)方法中有個(gè) ModelAndView 參數(shù),可以在此做一些修改動(dòng)作。
          afterCompletion(……) 方法:顧名思義,該方法是在整個(gè)請(qǐng)求處理完成后(包括視圖渲染)執(zhí)行,這時(shí)做一些資源的清理工作,這個(gè)方法只有在 preHandle(……) 被成功執(zhí)行后并且返回 true 才會(huì)被執(zhí)行。

          了解了該接口,接下來(lái)自定義一個(gè)攔截器。

          /**
           * 自定義攔截器
           * @author shengwu ni
           * @date 2018/08/03
           */

          public class MyInterceptor implements HandlerInterceptor {

              private static final Logger logger = LoggerFactory.getLogger(MyInterceptor.class);

              @Override
              public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

                  HandlerMethod handlerMethod = (HandlerMethod) handler;
                  Method method = handlerMethod.getMethod();
                  String methodName = method.getName();
                  logger.info("====攔截到了方法:{},在該方法執(zhí)行之前執(zhí)行====", methodName);
                  // 返回true才會(huì)繼續(xù)執(zhí)行,返回false則取消當(dāng)前請(qǐng)求
                  return true;
              }

              @Override
              public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
                  logger.info("執(zhí)行完方法之后進(jìn)執(zhí)行(Controller方法調(diào)用之后),但是此時(shí)還沒(méi)進(jìn)行視圖渲染");
              }

              @Override
              public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
                  logger.info("整個(gè)請(qǐng)求都處理完咯,DispatcherServlet也渲染了對(duì)應(yīng)的視圖咯,此時(shí)我可以做一些清理的工作了");
              }
          }

          OK,到此為止,攔截器已經(jīng)定義完成,接下來(lái)就是對(duì)該攔截器進(jìn)行攔截配置。

          1.2 配置攔截器

          在 Spring Boot 2.0 之前,我們都是直接繼承 WebMvcConfigurerAdapter 類,然后重寫 addInterceptors 方法來(lái)實(shí)現(xiàn)攔截器的配置。但是在 Spring Boot 2.0 之后,該方法已經(jīng)被廢棄了(當(dāng)然,也可以繼續(xù)用),取而代之的是 WebMvcConfigurationSupport 方法,如下:

          @Configuration
          public class MyInterceptorConfig extends WebMvcConfigurationSupport {

              @Override
              protected void addInterceptors(InterceptorRegistry registry) {
                  registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
                  super.addInterceptors(registry);
              }
          }

          在該配置中重寫 addInterceptors 方法,將我們上面自定義的攔截器添加進(jìn)去,addPathPatterns 方法是添加要攔截的請(qǐng)求,這里我們攔截所有的請(qǐng)求。這樣就配置好攔截器了,接下來(lái)寫一個(gè) Controller 測(cè)試一下:

          @Controller
          @RequestMapping("/interceptor")
          public class InterceptorController {

              @RequestMapping("/test")
              public String test() {
                  return "hello";
              }
          }

          讓其跳轉(zhuǎn)到 hello.html 頁(yè)面,直接在 hello.html 中輸出 hello interceptor 即可。啟動(dòng)項(xiàng)目,在瀏覽器中輸入 localhost:8080/interceptor/test 看一下控制臺(tái)的日志:

          ====攔截到了方法:test,在該方法執(zhí)行之前執(zhí)行====  
          執(zhí)行完方法之后進(jìn)執(zhí)行(Controller方法調(diào)用之后),但是此時(shí)還沒(méi)進(jìn)行視圖渲染  
          整個(gè)請(qǐng)求都處理完咯,DispatcherServlet也渲染了對(duì)應(yīng)的視圖咯,此時(shí)我可以做一些清理的工作了

          可以看出攔截器已經(jīng)生效,并能看出其執(zhí)行順序。

          1.3 解決靜態(tài)資源被攔截問(wèn)題

          上文中已經(jīng)介紹了攔截器的定義和配置,但是這樣是否就沒(méi)問(wèn)題了呢?其實(shí)不然,如果使用上面這種配置的話,我們會(huì)發(fā)現(xiàn)一個(gè)缺陷,那就是靜態(tài)資源被攔截了。可以在 resources/static/ 目錄下放置一個(gè)圖片資源或者 html 文件,然后啟動(dòng)項(xiàng)目直接訪問(wèn),即可看到無(wú)法訪問(wèn)的現(xiàn)象。

          也就是說(shuō),雖然 Spring Boot 2.0 廢棄了WebMvcConfigurerAdapter,但是 WebMvcConfigurationSupport 又會(huì)導(dǎo)致默認(rèn)的靜態(tài)資源被攔截,這就需要我們手動(dòng)將靜態(tài)資源放開(kāi)。

          如何放開(kāi)呢?除了在 MyInterceptorConfig 配置類中重寫 addInterceptors 方法外,還需要再重寫一個(gè)方法:addResourceHandlers,將靜態(tài)資源放開(kāi):

          /**
           * 用來(lái)指定靜態(tài)資源不被攔截,否則繼承WebMvcConfigurationSupport這種方式會(huì)導(dǎo)致靜態(tài)資源無(wú)法直接訪問(wèn)
           * @param registry
           */

          @Override
          protected void addResourceHandlers(ResourceHandlerRegistry registry) {
              registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
              super.addResourceHandlers(registry);
          }

          這樣配置好之后,重啟項(xiàng)目,靜態(tài)資源也可以正常訪問(wèn)了。如果你是個(gè)善于學(xué)習(xí)或者研究的人,那肯定不會(huì)止步于此,沒(méi)錯(cuò),上面這種方式的確能解決靜態(tài)資源無(wú)法訪問(wèn)的問(wèn)題,但是,還有更方便的方式來(lái)配置。

          我們不繼承 WebMvcConfigurationSupport 類,直接實(shí)現(xiàn) WebMvcConfigurer 接口,然后重寫 addInterceptors 方法,將自定義的攔截器添加進(jìn)去即可,如下:

          @Configuration
          public class MyInterceptorConfig implements WebMvcConfigurer {
              @Override
              public void addInterceptors(InterceptorRegistry registry) {
                  // 實(shí)現(xiàn)WebMvcConfigurer不會(huì)導(dǎo)致靜態(tài)資源被攔截
                  registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
              }
          }

          這樣就非常方便了,實(shí)現(xiàn) WebMvcConfigure 接口的話,不會(huì)攔截 Spring Boot 默認(rèn)的靜態(tài)資源。

          這兩種方式都可以,具體他們之間的細(xì)節(jié),感興趣的讀者可以做進(jìn)一步的研究,由于這兩種方式的不同,繼承 WebMvcConfigurationSupport 類的方式可以用在前后端分離的項(xiàng)目中,后臺(tái)不需要訪問(wèn)靜態(tài)資源(就不需要放開(kāi)靜態(tài)資源了);實(shí)現(xiàn) WebMvcConfigure 接口的方式可以用在非前后端分離的項(xiàng)目中,因?yàn)樾枰x取一些圖片、css、js文件等等。

          2. 攔截器使用實(shí)例

          2.1 判斷用戶有沒(méi)有登錄

          一般用戶登錄功能我們可以這么做,要么往 session 中寫一個(gè) user,要么針對(duì)每個(gè) user 生成一個(gè) token,第二種要更好一點(diǎn),那么針對(duì)第二種方式,如果用戶登錄成功了,每次請(qǐng)求的時(shí)候都會(huì)帶上該用戶的 token,如果未登錄,則沒(méi)有該 token,服務(wù)端可以檢測(cè)這個(gè) token 參數(shù)的有無(wú)來(lái)判斷用戶有沒(méi)有登錄,從而實(shí)現(xiàn)攔截功能。我們改造一下 preHandle 方法,如下:

          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

              HandlerMethod handlerMethod = (HandlerMethod) handler;
              Method method = handlerMethod.getMethod();
              String methodName = method.getName();
              logger.info("====攔截到了方法:{},在該方法執(zhí)行之前執(zhí)行====", methodName);

              // 判斷用戶有沒(méi)有登陸,一般登陸之后的用戶都有一個(gè)對(duì)應(yīng)的token
              String token = request.getParameter("token");
              if (null == token || "".equals(token)) {
                  logger.info("用戶未登錄,沒(méi)有權(quán)限執(zhí)行……請(qǐng)登錄");
                  return false;
              }

              // 返回true才會(huì)繼續(xù)執(zhí)行,返回false則取消當(dāng)前請(qǐng)求
              return true;
          }

          重啟項(xiàng)目,在瀏覽器中輸入 localhost:8080/interceptor/test 后查看控制臺(tái)日志,發(fā)現(xiàn)被攔截,如果在瀏覽器中輸入 localhost:8080/interceptor/test?token=123 即可正常往下走。

          2.2 取消攔截操作

          根據(jù)上文,如果我要攔截所有 /admin 開(kāi)頭的 url 請(qǐng)求的話,需要在攔截器配置中添加這個(gè)前綴,但是在實(shí)際項(xiàng)目中,可能會(huì)有這種場(chǎng)景出現(xiàn):某個(gè)請(qǐng)求也是 /admin 開(kāi)頭的,但是不能攔截,比如 /admin/login 等等,這樣的話又需要去配置。那么,可不可以做成一個(gè)類似于開(kāi)關(guān)的東西,哪里不需要攔截,我就在哪里弄個(gè)開(kāi)關(guān)上去,做成這種靈活的可插拔的效果呢?

          是可以的,我們可以定義一個(gè)注解,該注解專門用來(lái)取消攔截操作,如果某個(gè) Controller 中的方法我們不需要攔截掉,即可在該方法上加上我們自定義的注解即可,下面先定義一個(gè)注解:

          /**
           * 該注解用來(lái)指定某個(gè)方法不用攔截
           */

          @Target(ElementType.METHOD)
          @Retention(RetentionPolicy.RUNTIME)
          public @interface UnInterception {
          }

          然后在 Controller 中的某個(gè)方法上添加該注解,在攔截器處理方法中添加該注解取消攔截的邏輯,如下:

          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

              HandlerMethod handlerMethod = (HandlerMethod) handler;
              Method method = handlerMethod.getMethod();
              String methodName = method.getName();
              logger.info("====攔截到了方法:{},在該方法執(zhí)行之前執(zhí)行====", methodName);

              // 通過(guò)方法,可以獲取該方法上的自定義注解,然后通過(guò)注解來(lái)判斷該方法是否要被攔截
              // @UnInterception 是我們自定義的注解
              UnInterception unInterception = method.getAnnotation(UnInterception.class);
              if (null != unInterception) {
                  return true;
              }
              // 返回true才會(huì)繼續(xù)執(zhí)行,返回false則取消當(dāng)前請(qǐng)求
              return true;
          }

          Controller 中的方法代碼可以參見(jiàn)源碼,重啟項(xiàng)目在瀏覽器中輸入 http://localhost:8080/interceptor/test2?token=123 測(cè)試一下,可以看出,加了該注解的方法不會(huì)被攔截。

          3. 總結(jié)

          本節(jié)主要介紹了 Spring Boot 中攔截器的使用,從攔截器的創(chuàng)建、配置,到攔截器對(duì)靜態(tài)資源的影響,都做了詳細(xì)的分析。Spring Boot 2.0 之后攔截器的配置支持兩種方式,可以根據(jù)實(shí)際情況選擇不同的配置方式。最后結(jié)合實(shí)際中的使用,舉了兩個(gè)常用的場(chǎng)景,希望讀者能夠認(rèn)真消化,掌握攔截器的使用。

          -End-

          最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

          點(diǎn)擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

          在看點(diǎn)這里好文分享給更多人↓↓

          瀏覽 24
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  伊人综合大香蕉 | 国产99欧洲在线 | 亚洲视频免费观看H | 豆花无码在线 | 91毛片在线观看 |