日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
springboot實現(xiàn)攔截器的3種方式介紹及異步執(zhí)行的思考

[[411912]]

springboot 攔截器

實際項目中,我們經(jīng)常需要輸出請求參數(shù),響應(yīng)結(jié)果,方法耗時,統(tǒng)一的權(quán)限校驗等。

武昌ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:13518219792(備注:SSL證書合作)期待與您的合作!

本文首先為大家介紹 HTTP 請求中三種常見的攔截實現(xiàn),并且比較一下其中的差異。

(1)基于 Aspect 的攔截器

(2)基于 HandlerInterceptor 的攔截器

(3)基于 ResponseBodyAdvice 的攔截器

springboot 入門案例

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

maven 引入

引入必須的 jar 包。

 
 
 
 
  1.  
  2.     org.springframework.boot 
  3.     spring-boot-starter-parent 
  4.     1.5.9.RELEASE 
  5.  
  6.  
  7.  
  8.      
  9.         org.springframework.boot 
  10.         spring-boot-starter-web 
  11.      
  12.      
  13.         org.aspectj 
  14.         aspectjrt 
  15.         1.8.10 
  16.      
  17.      
  18.         org.aspectj 
  19.         aspectjweaver 
  20.         1.8.10 
  21.      
  22.  
  23.  
  24.  
  25.      
  26.          
  27.             org.springframework.boot 
  28.             spring-boot-maven-plugin 
  29.          
  30.      
  31.  

啟動類

實現(xiàn)最簡單的啟動類。

 
 
 
 
  1. @SpringBootApplication 
  2. public class Application { 
  3.  
  4.     public static void main(String[] args) { 
  5.         SpringApplication.run(Application.class, args); 
  6.     } 
  7.  

定義 Controller

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

 
 
 
 
  1. @RestController 
  2. public class IndexController { 
  3.  
  4.     @RequestMapping("/index") 
  5.     public AsyncResp index() { 
  6.         AsyncResp asyncResp = new AsyncResp(); 
  7.         asyncResp.setResult("ok"); 
  8.         asyncResp.setRespCode("00"); 
  9.         asyncResp.setRespDesc("成功"); 
  10.  
  11.         System.out.println("IndexController#index:" + asyncResp); 
  12.         return asyncResp; 
  13.     } 
  14.  

其中 AsyncResp 的定義如下:

 
 
 
 
  1. public class AsyncResp { 
  2.  
  3.     private String respCode; 
  4.  
  5.     private String respDesc; 
  6.  
  7.     private String result; 
  8.  
  9.  
  10.     // getter & setter & toString() 

攔截器定義

基于 Aspect

 
 
 
 
  1. import org.aspectj.lang.ProceedingJoinPoint; 
  2. import org.aspectj.lang.annotation.Around; 
  3. import org.aspectj.lang.annotation.Aspect; 
  4. import org.aspectj.lang.annotation.Pointcut; 
  5. import org.slf4j.Logger; 
  6. import org.slf4j.LoggerFactory; 
  7. import org.springframework.context.annotation.EnableAspectJAutoProxy; 
  8. import org.springframework.stereotype.Component; 
  9.  
  10. import java.util.Arrays; 
  11.  
  12. /** 
  13.  * 
  14.  * @author binbin.hou 
  15.  * @since 1.0.0 
  16.  */ 
  17. @Aspect 
  18. @Component 
  19. @EnableAspectJAutoProxy 
  20. public class AspectLogInterceptor { 
  21.  
  22.     /** 
  23.      * 日志實例 
  24.      * @since 1.0.0 
  25.      */ 
  26.     private static final Logger LOG = LoggerFactory.getLogger(AspectLogInterceptor.class); 
  27.  
  28.     /** 
  29.      * 攔截 controller 下所有的 public方法 
  30.      */ 
  31.     @Pointcut("execution(public * com.github.houbb.springboot.learn.aspect.controller..*(..))") 
  32.     public void pointCut() { 
  33.         // 
  34.     } 
  35.  
  36.     /** 
  37.      * 攔截處理 
  38.      * 
  39.      * @param point point 信息 
  40.      * @return result 
  41.      * @throws Throwable if any 
  42.      */ 
  43.     @Around("pointCut()") 
  44.     public Object around(ProceedingJoinPoint point) throws Throwable { 
  45.         try { 
  46.             //1. 設(shè)置 MDC 
  47.  
  48.             // 獲取當前攔截的方法簽名 
  49.             String signatureShortStr = point.getSignature().toShortString(); 
  50.             //2. 打印入?yún)⑿畔?nbsp;
  51.             Object[] args = point.getArgs(); 
  52.             LOG.info("{} 參數(shù): {}", signatureShortStr, Arrays.toString(args)); 
  53.  
  54.             //3. 打印結(jié)果 
  55.             Object result = point.proceed(); 
  56.             LOG.info("{} 結(jié)果: {}", signatureShortStr, result); 
  57.             return result; 
  58.         } finally { 
  59.             // 移除 mdc 
  60.         } 
  61.     } 
  62.  

這種實現(xiàn)的優(yōu)點是比較通用,可以結(jié)合注解實現(xiàn)更加靈活強大的功能。

是個人非常喜歡的一種方式。

主要用途:

(1)日志的出參/入?yún)?/p>

(2)統(tǒng)一設(shè)置 TraceId

(3)方法的調(diào)用耗時統(tǒng)計

基于 HandlerInterceptor

 
 
 
 
  1. import org.slf4j.Logger; 
  2. import org.slf4j.LoggerFactory; 
  3. import org.springframework.stereotype.Component; 
  4. import org.springframework.web.servlet.HandlerInterceptor; 
  5. import org.springframework.web.servlet.ModelAndView; 
  6.  
  7. import javax.servlet.DispatcherType; 
  8. import javax.servlet.http.HttpServletRequest; 
  9. import javax.servlet.http.HttpServletResponse; 
  10.  
  11. /** 
  12.  * @author binbin.hou 
  13.  * @since 1.0.0 
  14.  */ 
  15. @Component 
  16. public class LogHandlerInterceptor implements HandlerInterceptor { 
  17.  
  18.     private Logger logger = LoggerFactory.getLogger(LogHandlerInterceptor.class); 
  19.  
  20.     @Override 
  21.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 
  22.         // 統(tǒng)一的權(quán)限校驗、路由等 
  23.         logger.info("LogHandlerInterceptor#preHandle 請求地址:{}", request.getRequestURI()); 
  24.  
  25.         if (request.getDispatcherType().equals(DispatcherType.ASYNC)) { 
  26.             return true; 
  27.         } 
  28.  
  29.         return true; 
  30.     } 
  31.  
  32.     @Override 
  33.     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 
  34.         logger.info("LogHandlerInterceptor#postHandle 調(diào)用"); 
  35.     } 
  36.  
  37.     @Override 
  38.     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 
  39.  
  40.     } 
  41.  

然后需要指定對應(yīng)的 url 和攔截器之間的關(guān)系才會生效:

 
 
 
 
  1. import com.github.houbb.springboot.learn.aspect.aspect.LogHandlerInterceptor; 
  2. import org.springframework.beans.factory.annotation.Autowired; 
  3. import org.springframework.context.annotation.Configuration; 
  4. import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 
  5. import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 
  6.  
  7. /** 
  8.  * spring mvc 配置 
  9.  * @since 1.0.0 
  10.  */ 
  11. @Configuration 
  12. public class SpringMvcConfig extends WebMvcConfigurerAdapter { 
  13.  
  14.     @Autowired 
  15.     private LogHandlerInterceptor logHandlerInterceptor; 
  16.  
  17.     @Override 
  18.     public void addInterceptors(InterceptorRegistry registry) { 
  19.         registry.addInterceptor(logHandlerInterceptor) 
  20.                 .addPathPatterns("/**") 
  21.                 .excludePathPatterns("/version"); 
  22.         super.addInterceptors(registry); 
  23.     } 
  24.  

這種方式的優(yōu)點就是可以根據(jù) url 靈活指定不同的攔截器。

缺點是主要用于 Controller 層。

基于 ResponseBodyAdvice

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

比如加密,簽名等。

 
 
 
 
  1. import org.slf4j.Logger; 
  2. import org.slf4j.LoggerFactory; 
  3. import org.springframework.core.MethodParameter; 
  4. import org.springframework.http.MediaType; 
  5. import org.springframework.http.converter.HttpMessageConverter; 
  6. import org.springframework.http.server.ServerHttpRequest; 
  7. import org.springframework.http.server.ServerHttpResponse; 
  8. import org.springframework.http.server.ServletServerHttpRequest; 
  9. import org.springframework.web.bind.annotation.ControllerAdvice; 
  10. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; 
  11.  
  12. import javax.servlet.http.HttpServletRequest; 
  13.  
  14. /** 
  15.  * @author binbin.hou 
  16.  * @since 1.0.0 
  17.  */ 
  18. @ControllerAdvice 
  19. public class MyResponseBodyAdvice implements ResponseBodyAdvice { 
  20.  
  21.     /** 
  22.      * 日志實例 
  23.      * @since 1.0.0 
  24.      */ 
  25.     private static final Logger LOG = LoggerFactory.getLogger(MyResponseBodyAdvice.class); 
  26.  
  27.     @Override 
  28.     public boolean supports(MethodParameter methodParameter, Class aClass) { 
  29.         //這個地方如果返回false, 不會執(zhí)行 beforeBodyWrite 方法 
  30.         return true; 
  31.     } 
  32.  
  33.     @Override 
  34.     public Object beforeBodyWrite(Object resp, MethodParameter methodParameter, MediaType mediaType, Class> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { 
  35.         String uri = serverHttpRequest.getURI().getPath(); 
  36.         LOG.info("MyResponseBodyAdvice#beforeBodyWrite 請求地址:{}", uri); 
  37.  
  38.         ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) serverHttpRequest; 
  39.         HttpServletRequest servletRequest = servletServerHttpRequest.getServletRequest(); 
  40.  
  41.         // 可以做統(tǒng)一的攔截器處理 
  42.  
  43.         // 可以對結(jié)果做動態(tài)修改等 
  44.         LOG.info("MyResponseBodyAdvice#beforeBodyWrite 響應(yīng)結(jié)果:{}", resp); 
  45.         return resp; 
  46.     } 
  47.  
  48. 測試

    我們啟動應(yīng)用,頁面訪問:

    http://localhost:18080/index

    頁面響應(yīng):

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

    后端日志:

     
     
     
     
    1. c.g.h.s.l.a.a.LogHandlerInterceptor      : LogHandlerInterceptor#preHandle 請求地址:/index 
    2. c.g.h.s.l.a.aspect.AspectLogInterceptor  : IndexController.index() 參數(shù): [] 
    3. IndexController#index:AsyncResp{respCode='00', respDesc='成功', result='ok'} 
    4. c.g.h.s.l.a.aspect.AspectLogInterceptor  : IndexController.index() 結(jié)果: AsyncResp{respCode='00', respDesc='成功', result='ok'} 
    5. c.g.h.s.l.a.aspect.MyResponseBodyAdvice  : MyResponseBodyAdvice#beforeBodyWrite 請求地址:/index 
    6. c.g.h.s.l.a.aspect.MyResponseBodyAdvice  : MyResponseBodyAdvice#beforeBodyWrite 響應(yīng)結(jié)果:AsyncResp{respCode='00', respDesc='成功', result='ok'} 
    7. c.g.h.s.l.a.a.LogHandlerInterceptor      : LogHandlerInterceptor#postHandle 調(diào)用 

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

    異步執(zhí)行

    當然,如果只是上面這些內(nèi)容,并不是本篇文章的重點。

    接下來,我們一起來看下,如果引入了異步執(zhí)行會怎么樣。

    定義異步線程池

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

     
     
     
     
    1. import org.springframework.context.annotation.Bean; 
    2. import org.springframework.context.annotation.Configuration; 
    3. import org.springframework.core.task.AsyncTaskExecutor; 
    4. import org.springframework.scheduling.annotation.EnableAsync; 
    5. import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 
    6.  
    7. /** 
    8.  * 請求異步處理配置 
    9.  * 
    10.  * @author binbin.hou 
    11.  */ 
    12. @Configuration 
    13. @EnableAsync 
    14. public class SpringAsyncConfig { 
    15.  
    16.     @Bean(name = "asyncPoolTaskExecutor") 
    17.     public AsyncTaskExecutor taskExecutor() { 
    18.         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 
    19.         executor.setMaxPoolSize(10); 
    20.         executor.setQueueCapacity(10); 
    21.         executor.setCorePoolSize(10); 
    22.         executor.setWaitForTasksToCompleteOnShutdown(true); 
    23.         return executor; 
    24.     } 
    25.  

    異步執(zhí)行的 Controller

     
     
     
     
    1. @RestController 
    2. public class MyAsyncController extends BaseAsyncController { 
    3.  
    4.     @Override 
    5.     protected String process(HttpServletRequest request) { 
    6.         return "ok"; 
    7.     } 
    8.  
    9.     @RequestMapping("/async") 
    10.     public AsyncResp hello(HttpServletRequest request) { 
    11.         AsyncResp resp = super.execute(request); 
    12.  
    13.         System.out.println("Controller#async 結(jié)果:" + resp); 
    14.         return resp; 
    15.     } 
    16.  

     其中 BaseAsyncController 的實現(xiàn)如下:

     
     
     
     
    1. @RestController 
    2. public abstract class BaseAsyncController { 
    3.  
    4.     protected abstract T process(HttpServletRequest request); 
    5.  
    6.     @Autowired 
    7.     private AsyncTaskExecutor taskExecutor; 
    8.  
    9.     protected AsyncResp execute(HttpServletRequest request) { 
    10.         // 異步響應(yīng)結(jié)果 
    11.         AsyncResp resp = new AsyncResp(); 
    12.         try { 
    13.             taskExecutor.execute(new Runnable() { 
    14.                 @Override 
    15.                 public void run() { 
    16.                     try { 
    17.                         T result = process(request); 
    18.  
    19.                         resp.setRespCode("00"); 
    20.                         resp.setRespDesc("成功"); 
    21.                         resp.setResult(result.toString()); 
    22.  
    23.                     } catch (Exception exception) { 
    24.                         resp.setRespCode("98"); 
    25.                         resp.setRespDesc("任務(wù)異常"); 
    26.                     } 
    27.                 } 
    28.             }); 
    29.         } catch (TaskRejectedException e) { 
    30.             resp.setRespCode("99"); 
    31.             resp.setRespDesc("任務(wù)拒絕"); 
    32.         } 
    33.  
    34.         return resp; 
    35.     } 
    36.  

     execute 的實現(xiàn)也比較簡單:

    (1)主線程創(chuàng)建一個 AsyncResp,用于返回。

    (2)線程池異步執(zhí)行具體的子類方法,并且設(shè)置對應(yīng)的值。

    思考

    接下來,問大家一個問題。

    如果我們請求

    http://localhost:18080/async,那么:

    (1)頁面得到的返回值是什么?

    (2)Aspect 日志輸出的返回值是?

    (3)ResponseBodyAdvice 日志輸出的返回值是什么?

    你可以在這里稍微停一下,記錄下你的答案。

    測試

    我們頁面請求

    http://localhost:18080/async。

    頁面響應(yīng)如下:

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

    后端的日志:

     
     
     
     
    1. c.g.h.s.l.a.a.LogHandlerInterceptor      : LogHandlerInterceptor#preHandle 請求地址:/async 
    2. c.g.h.s.l.a.aspect.AspectLogInterceptor  : MyAsyncController.hello(..) 參數(shù): [org.apache.catalina.connector.RequestFacade@7e931750] 
    3. Controller#async 結(jié)果:AsyncResp{respCode='null', respDesc='null', result='null'} 
    4. c.g.h.s.l.a.aspect.AspectLogInterceptor  : MyAsyncController.hello(..) 結(jié)果: AsyncResp{respCode='null', respDesc='null', result='null'} 
    5. c.g.h.s.l.a.aspect.MyResponseBodyAdvice  : MyResponseBodyAdvice#beforeBodyWrite 請求地址:/async 
    6. c.g.h.s.l.a.aspect.MyResponseBodyAdvice  : MyResponseBodyAdvice#beforeBodyWrite 響應(yīng)結(jié)果:AsyncResp{respCode='00', respDesc='成功', result='ok'} 
    7. c.g.h.s.l.a.a.LogHandlerInterceptor      : LogHandlerInterceptor#postHandle 調(diào)用 

    對比一下,可以發(fā)現(xiàn)我們上面問題的答案:

    (1)頁面得到的返回值是什么?

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

    可以獲取到異步執(zhí)行完成的結(jié)果。

    (2)Aspect 日志輸出的返回值是?

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

    無法獲取異步結(jié)果。

    (3)ResponseBodyAdvice 日志輸出的返回值是什么?

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

    可以獲取到異步執(zhí)行完成的結(jié)果。

    反思

    可以發(fā)現(xiàn),spring 對于頁面的響應(yīng)也許和我們想的有些不一樣,并不是直接獲取同步結(jié)果。

    寫到這里,發(fā)現(xiàn)自己對于 mvc 的理解一直只是停留在表面,沒有真正理解整個流程。

    Aspect 的形式在很多框架中都會使用,不過這里會發(fā)現(xiàn)無法獲取異步的執(zhí)行結(jié)果,存在一定問題。


    新聞名稱:springboot實現(xiàn)攔截器的3種方式介紹及異步執(zhí)行的思考
    當前URL:http://www.dlmjj.cn/article/cocijos.html