中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

springboot有哪些實現攔截器的方式及異步執行是什么

發布時間:2021-07-09 11:16:17 來源:億速云 閱讀:256 作者:chen 欄目:開發技術

這篇文章主要講解了“springboot有哪些實現攔截器的方式及異步執行是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“springboot有哪些實現攔截器的方式及異步執行是什么”吧!

目錄
  • springboot 攔截器

  • springboot 入門案例

    • maven 引入

    • 啟動類

    • 定義 Controller

  • 攔截器定義

    • 基于 Aspect

  • 基于 HandlerInterceptor

    • 基于 ResponseBodyAdvice

      • 測試

    • 異步執行

      • 定義異步線程池

      • 異步執行的 Controller

      • 思考

      • 測試

    • 反思

      springboot 攔截器

      實際項目中,我們經常需要輸出請求參數,響應結果,方法耗時,統一的權限校驗等。

      本文首先為大家介紹 HTTP 請求中三種常見的攔截實現,并且比較一下其中的差異。
      (1)基于 Aspect 的攔截器
      (2)基于 HandlerInterceptor 的攔截器
      (3)基于 ResponseBodyAdvice 的攔截器

      推薦閱讀:

      統一日志框架: https://github.com/houbb/auto-log

      springboot有哪些實現攔截器的方式及異步執行是什么

      springboot 入門案例

      為了便于大家學習,我們首先從最基本的 springboot 例子講起。

      maven 引入

      引入必須的 jar 包。

      <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-parent</artifactId>
          <version>1.5.9.RELEASE</version>
      </parent>
      
      <dependencies>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-web</artifactId>
          </dependency>
          <dependency>
              <groupId>org.aspectj</groupId>
              <artifactId>aspectjrt</artifactId>
              <version>1.8.10</version>
          </dependency>
          <dependency>
              <groupId>org.aspectj</groupId>
              <artifactId>aspectjweaver</artifactId>
              <version>1.8.10</version>
          </dependency>
      </dependencies>
      <!-- Package as an executable jar -->
      <build>
          <plugins>
              <plugin>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-maven-plugin</artifactId>
              </plugin>
          </plugins>
      </build>

      啟動類

      實現最簡單的啟動類。

      @SpringBootApplication
      public class Application {
      
          public static void main(String[] args) {
              SpringApplication.run(Application.class, args);
          }
      
      }

      定義 Controller

      為了演示方便,我們首先實現一個簡單的 controller。

      @RestController
      public class IndexController {
      
          @RequestMapping("/index")
          public AsyncResp index() {
              AsyncResp asyncResp = new AsyncResp();
              asyncResp.setResult("ok");
              asyncResp.setRespCode("00");
              asyncResp.setRespDesc("成功");
      
              System.out.println("IndexController#index:" + asyncResp);
              return asyncResp;
          }
      
      }

      其中 AsyncResp 的定義如下:

      public class AsyncResp {
      
          private String respCode;
      
          private String respDesc;
      
          private String result;
      
      
          // getter & setter & toString()
      }

      攔截器定義

      基于 Aspect

      import org.aspectj.lang.ProceedingJoinPoint;
      import org.aspectj.lang.annotation.Around;
      import org.aspectj.lang.annotation.Aspect;
      import org.aspectj.lang.annotation.Pointcut;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.context.annotation.EnableAspectJAutoProxy;
      import org.springframework.stereotype.Component;
      
      import java.util.Arrays;
      
      /**
       *
       * @author binbin.hou
       * @since 1.0.0
       */
      @Aspect
      @Component
      @EnableAspectJAutoProxy
      public class AspectLogInterceptor {
      
          /**
           * 日志實例
           * @since 1.0.0
           */
          private static final Logger LOG = LoggerFactory.getLogger(AspectLogInterceptor.class);
      
          /**
           * 攔截 controller 下所有的 public方法
           */
          @Pointcut("execution(public * com.github.houbb.springboot.learn.aspect.controller..*(..))")
          public void pointCut() {
              //
          }
      
          /**
           * 攔截處理
           *
           * @param point point 信息
           * @return result
           * @throws Throwable if any
           */
          @Around("pointCut()")
          public Object around(ProceedingJoinPoint point) throws Throwable {
              try {
                  //1. 設置 MDC
      
                  // 獲取當前攔截的方法簽名
                  String signatureShortStr = point.getSignature().toShortString();
                  //2. 打印入參信息
                  Object[] args = point.getArgs();
                  LOG.info("{} 參數: {}", signatureShortStr, Arrays.toString(args));
      
                  //3. 打印結果
                  Object result = point.proceed();
                  LOG.info("{} 結果: {}", signatureShortStr, result);
                  return result;
              } finally {
                  // 移除 mdc
              }
          }
      }

      這種實現的優點是比較通用,可以結合注解實現更加靈活強大的功能。

      是個人非常喜歡的一種方式。
      主要用途:
      (1)日志的出參/入參
      (2)統一設置 TraceId
      (3)方法的調用耗時統計

      基于 HandlerInterceptor

      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.stereotype.Component;
      import org.springframework.web.servlet.HandlerInterceptor;
      import org.springframework.web.servlet.ModelAndView;
      
      import javax.servlet.DispatcherType;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      /**
       * @author binbin.hou
       * @since 1.0.0
       */
      @Component
      public class LogHandlerInterceptor implements HandlerInterceptor {
      
          private Logger logger = LoggerFactory.getLogger(LogHandlerInterceptor.class);
      
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              // 統一的權限校驗、路由等
              logger.info("LogHandlerInterceptor#preHandle 請求地址:{}", request.getRequestURI());
      
              if (request.getDispatcherType().equals(DispatcherType.ASYNC)) {
                  return true;
              }
      
              return true;
          }
      
          @Override
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
              logger.info("LogHandlerInterceptor#postHandle 調用");
          }
      
          @Override
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
      
          }
      }

      然后需要指定對應的 url 和攔截器之間的關系才會生效:

      import com.github.houbb.springboot.learn.aspect.aspect.LogHandlerInterceptor;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
      import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
      
      /**
       * spring mvc 配置
       * @since 1.0.0
       */
      @Configuration
      public class SpringMvcConfig extends WebMvcConfigurerAdapter {
      
          @Autowired
          private LogHandlerInterceptor logHandlerInterceptor;
      
          @Override
          public void addInterceptors(InterceptorRegistry registry) {
              registry.addInterceptor(logHandlerInterceptor)
                      .addPathPatterns("/**")
                      .excludePathPatterns("/version");
              super.addInterceptors(registry);
          }
      }

      這種方式的優點就是可以根據 url 靈活指定不同的攔截器。
      缺點是主要用于 Controller 層。

      基于 ResponseBodyAdvice

      此接口有beforeBodyWrite方法,參數body是響應對象response中的響應體,那么我們就可以用此方法來對響應體做一些統一的操作。

      比如加密,簽名等。

      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.core.MethodParameter;
      import org.springframework.http.MediaType;
      import org.springframework.http.converter.HttpMessageConverter;
      import org.springframework.http.server.ServerHttpRequest;
      import org.springframework.http.server.ServerHttpResponse;
      import org.springframework.http.server.ServletServerHttpRequest;
      import org.springframework.web.bind.annotation.ControllerAdvice;
      import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
      
      import javax.servlet.http.HttpServletRequest;
      
      /**
       * @author binbin.hou
       * @since 1.0.0
       */
      @ControllerAdvice
      public class MyResponseBodyAdvice implements ResponseBodyAdvice<Object> {
      
          /**
           * 日志實例
           * @since 1.0.0
           */
          private static final Logger LOG = LoggerFactory.getLogger(MyResponseBodyAdvice.class);
      
          @Override
          public boolean supports(MethodParameter methodParameter, Class aClass) {
              //這個地方如果返回false, 不會執行 beforeBodyWrite 方法
              return true;
          }
      
          @Override
          public Object beforeBodyWrite(Object resp, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
              String uri = serverHttpRequest.getURI().getPath();
              LOG.info("MyResponseBodyAdvice#beforeBodyWrite 請求地址:{}", uri);
      
              ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) serverHttpRequest;
              HttpServletRequest servletRequest = servletServerHttpRequest.getServletRequest();
      
              // 可以做統一的攔截器處理
      
              // 可以對結果做動態修改等
              LOG.info("MyResponseBodyAdvice#beforeBodyWrite 響應結果:{}", resp);
              return resp;
          }
      }

      測試

      我們啟動應用,頁面訪問:
      http://localhost:18080/index
      頁面響應:
      {"respCode":"00","respDesc":"成功","result":"ok"}

      后端日志:

      c.g.h.s.l.a.a.LogHandlerInterceptor      : LogHandlerInterceptor#preHandle 請求地址:/index
      c.g.h.s.l.a.aspect.AspectLogInterceptor  : IndexController.index() 參數: []
      IndexController#index:AsyncResp{respCode='00', respDesc='成功', result='ok'}
      c.g.h.s.l.a.aspect.AspectLogInterceptor  : IndexController.index() 結果: AsyncResp{respCode='00', respDesc='成功', result='ok'}
      c.g.h.s.l.a.aspect.MyResponseBodyAdvice  : MyResponseBodyAdvice#beforeBodyWrite 請求地址:/index
      c.g.h.s.l.a.aspect.MyResponseBodyAdvice  : MyResponseBodyAdvice#beforeBodyWrite 響應結果:AsyncResp{respCode='00', respDesc='成功', result='ok'}
      c.g.h.s.l.a.a.LogHandlerInterceptor      : LogHandlerInterceptor#postHandle 調用

      這里執行的先后順序也比較明確,此處不再贅述。

      異步執行

      當然,如果只是上面這些內容,并不是本篇文章的重點。
      接下來,我們一起來看下,如果引入了異步執行會怎么樣。

      定義異步線程池

      springboot 中定義異步線程池,非常簡單。

      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.core.task.AsyncTaskExecutor;
      import org.springframework.scheduling.annotation.EnableAsync;
      import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
      
      /**
       * 請求異步處理配置
       *
       * @author binbin.hou
       */
      @Configuration
      @EnableAsync
      public class SpringAsyncConfig {
      
          @Bean(name = "asyncPoolTaskExecutor")
          public AsyncTaskExecutor taskExecutor() {
              ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
              executor.setMaxPoolSize(10);
              executor.setQueueCapacity(10);
              executor.setCorePoolSize(10);
              executor.setWaitForTasksToCompleteOnShutdown(true);
              return executor;
          }
      
      }

      異步執行的 Controller

      @RestController
      public class MyAsyncController extends BaseAsyncController<String> {
      
          @Override
          protected String process(HttpServletRequest request) {
              return "ok";
          }
      
          @RequestMapping("/async")
          public AsyncResp hello(HttpServletRequest request) {
              AsyncResp resp = super.execute(request);
      
              System.out.println("Controller#async 結果:" + resp);
              return resp;
          }
      }

      其中 BaseAsyncController 的實現如下:

      @RestController
      public abstract class BaseAsyncController<T> {
      
          protected abstract T process(HttpServletRequest request);
      
          @Autowired
          private AsyncTaskExecutor taskExecutor;
      
          protected AsyncResp execute(HttpServletRequest request) {
              // 異步響應結果
              AsyncResp resp = new AsyncResp();
              try {
                  taskExecutor.execute(new Runnable() {
                      @Override
                      public void run() {
                          try {
                              T result = process(request);
      
                              resp.setRespCode("00");
                              resp.setRespDesc("成功");
                              resp.setResult(result.toString());
      
                          } catch (Exception exception) {
                              resp.setRespCode("98");
                              resp.setRespDesc("任務異常");
                          }
                      }
                  });
              } catch (TaskRejectedException e) {
                  resp.setRespCode("99");
                  resp.setRespDesc("任務拒絕");
              }
      
              return resp;
          }
      }

      execute 的實現也比較簡單:
      (1)主線程創建一個 AsyncResp,用于返回。
      (2)線程池異步執行具體的子類方法,并且設置對應的值。

      思考

      接下來,問大家一個問題。
      如果我們請求 http://localhost:18080/async,那么:
      (1)頁面得到的返回值是什么?
      (2)Aspect 日志輸出的返回值是?
      (3)ResponseBodyAdvice 日志輸出的返回值是什么?
      你可以在這里稍微停一下,記錄下你的答案。

      測試

      我們頁面請求 http://localhost:18080/async。

      頁面響應如下:

      {"respCode":"00","respDesc":"成功","result":"ok"}

      后端的日志:

      c.g.h.s.l.a.a.LogHandlerInterceptor      : LogHandlerInterceptor#preHandle 請求地址:/async
      c.g.h.s.l.a.aspect.AspectLogInterceptor  : MyAsyncController.hello(..) 參數: [org.apache.catalina.connector.RequestFacade@7e931750]
      Controller#async 結果:AsyncResp{respCode='null', respDesc='null', result='null'}
      c.g.h.s.l.a.aspect.AspectLogInterceptor  : MyAsyncController.hello(..) 結果: AsyncResp{respCode='null', respDesc='null', result='null'}
      c.g.h.s.l.a.aspect.MyResponseBodyAdvice  : MyResponseBodyAdvice#beforeBodyWrite 請求地址:/async
      c.g.h.s.l.a.aspect.MyResponseBodyAdvice  : MyResponseBodyAdvice#beforeBodyWrite 響應結果:AsyncResp{respCode='00', respDesc='成功', result='ok'}
      c.g.h.s.l.a.a.LogHandlerInterceptor      : LogHandlerInterceptor#postHandle 調用

      對比一下,可以發現我們上面問題的答案:
      (1)頁面得到的返回值是什么?

      {"respCode":"00","respDesc":"成功","result":"ok"}

      可以獲取到異步執行完成的結果。
      (2)Aspect 日志輸出的返回值是?

      AsyncResp{respCode='null', respDesc='null', result='null'}

      無法獲取異步結果。
      (3)ResponseBodyAdvice 日志輸出的返回值是什么?

      AsyncResp{respCode='00', respDesc='成功', result='ok'}

      可以獲取到異步執行完成的結果。

      反思

      可以發現,spring 對于頁面的響應也許和我們想的有些不一樣,并不是直接獲取同步結果。
      寫到這里,發現自己對于 mvc 的理解一直只是停留在表面,沒有真正理解整個流程。
      Aspect 的形式在很多框架中都會使用,不過這里會發現無法獲取異步的執行結果,存在一定問題。

      感謝各位的閱讀,以上就是“springboot有哪些實現攔截器的方式及異步執行是什么”的內容了,經過本文的學習后,相信大家對springboot有哪些實現攔截器的方式及異步執行是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

      向AI問一下細節

      免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

      AI

      涡阳县| 淮阳县| 资源县| 乐东| 岐山县| 霍林郭勒市| 隆安县| 兴宁市| 伊宁县| 公安县| 汉阴县| 新化县| 临西县| 黄冈市| 德惠市| 新郑市| 平舆县| 乌兰县| 霍林郭勒市| 宜章县| 邛崃市| 新源县| 托克逊县| 肇源县| 怀远县| 金坛市| 深圳市| 黄平县| 宁乡县| 滕州市| 齐齐哈尔市| 竹溪县| 霍林郭勒市| 隆尧县| 英吉沙县| 长寿区| 突泉县| 关岭| 铜梁县| 乐陵市| 纳雍县|