成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

2行代碼,讓接口性能提升10倍

新聞 前端
logservice中有個log方法用來記錄日志,當調用logService.log(msg)的時候,希望異步執行,那么可以通過@EnableAsync & @Async來實現。

[[334572]]

 1、本文內容

詳解 @EnableAsync & @Async,主要分下面幾個點進行介紹。

  1. 作用
  2. 用法
  3. 獲取異步執行結果
  4. 自定義異步執行的線程池
  5. 自定義異常處理
  6. 線程隔離
  7. 源碼 & 原理

2、作用

spring容器中實現bean方法的異步調用。

比如有個logService的bean,logservice中有個log方法用來記錄日志,當調用logService.log(msg)的時候,希望異步執行,那么可以通過@EnableAsync & @Async來實現。

3、用法

2步

  1. 需要異步執行的方法上面使用@Async注解標注,若bean中所有的方法都需要異步執行,可以直接將@Async加載類上。
  2. 將@EnableAsync添加在spring配置類上,此時@Async注解才會起效。

常見2種用法

  1. 無返回值的
  2. 可以獲取返回值的

4、無返回值的

用法

方法返回值不是Future類型的,被執行時,會立即返回,并且無法獲取方法返回值,如:

  1. @Async 
  2. public void log(String msg) throws InterruptedException { 
  3.     System.out.println("開始記錄日志," + System.currentTimeMillis()); 
  4.     //模擬耗時2秒 
  5.     TimeUnit.SECONDS.sleep(2); 
  6.     System.out.println("日志記錄完畢," + System.currentTimeMillis()); 

案例

實現日志異步記錄的功能。

LogService.log方法用來異步記錄日志,需要使用@Async標注

  1. package com.javacode2018.async.demo1; 
  2.  
  3. import org.springframework.scheduling.annotation.Async; 
  4. import org.springframework.stereotype.Component; 
  5.  
  6. import java.util.concurrent.TimeUnit; 
  7.  
  8. @Component 
  9. public class LogService { 
  10.     @Async 
  11.     public void log(String msg) throws InterruptedException { 
  12.         System.out.println(Thread.currentThread() + "開始記錄日志," + System.currentTimeMillis()); 
  13.         //模擬耗時2秒 
  14.         TimeUnit.SECONDS.sleep(2); 
  15.         System.out.println(Thread.currentThread() + "日志記錄完畢," + System.currentTimeMillis()); 
  16.     } 

來個spring配置類,需要加上@EnableAsync開啟bean方法的異步調用.

  1. package com.javacode2018.async.demo1; 
  2.  
  3. import org.springframework.context.annotation.ComponentScan; 
  4. import org.springframework.context.annotation.EnableAspectJAutoProxy; 
  5. import org.springframework.scheduling.annotation.EnableAsync; 
  6.  
  7. @ComponentScan 
  8. @EnableAsync 
  9. public class MainConfig1 { 

測試代碼

  1. package com.javacode2018.async; 
  2.  
  3. import com.javacode2018.async.demo1.LogService; 
  4. import com.javacode2018.async.demo1.MainConfig1; 
  5. import org.junit.Test; 
  6. import org.springframework.context.annotation.AnnotationConfigApplicationContext; 
  7.  
  8. import java.util.concurrent.TimeUnit; 
  9.  
  10. public class AsyncTest { 
  11.  
  12.     @Test 
  13.     public void test1() throws InterruptedException { 
  14.         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); 
  15.         context.register(MainConfig1.class); 
  16.         context.refresh(); 
  17.         LogService logService = context.getBean(LogService.class); 
  18.         System.out.println(Thread.currentThread() + " logService.log start," + System.currentTimeMillis()); 
  19.         logService.log("異步執行方法!"); 
  20.         System.out.println(Thread.currentThread() + " logService.log end," + System.currentTimeMillis()); 
  21.  
  22.         //休眠一下,防止@Test退出 
  23.         TimeUnit.SECONDS.sleep(3); 
  24.     } 
  25.  

運行輸出

  1. Thread[main,5,main] logService.log start,1595223990417 
  2. Thread[main,5,main] logService.log end,1595223990432 
  3. Thread[SimpleAsyncTaskExecutor-1,5,main]開始記錄日志,1595223990443 
  4. Thread[SimpleAsyncTaskExecutor-1,5,main]日志記錄完畢,1595223992443 

前2行輸出,可以看出logService.log立即就返回了,后面2行來自于log方法,相差2秒左右。

前面2行在主線程中執行,后面2行在異步線程中執行。

5、獲取異步返回值

用法

若需取異步執行結果,方法返回值必須為Future類型,使用spring提供的靜態方法org.springframework.scheduling.annotation.AsyncResult#forValue創建返回值,如:

  1. public Future<String> getGoodsInfo(long goodsId) throws InterruptedException { 
  2.     return AsyncResult.forValue(String.format("商品%s基本信息!", goodsId)); 

案例

場景:電商中商品詳情頁通常會有很多信息:商品基本信息、商品描述信息、商品評論信息,通過3個方法來或者這幾個信息。

這3個方法之間無關聯,所以可以采用異步的方式并行獲取,提升效率。

下面是商品服務,內部3個方法都需要異步,所以直接在類上使用@Async標注了,每個方法內部休眠500毫秒,模擬一下耗時操作。

  1. package com.javacode2018.async.demo2; 
  2.  
  3. import org.springframework.scheduling.annotation.Async; 
  4. import org.springframework.scheduling.annotation.AsyncResult; 
  5. import org.springframework.stereotype.Component; 
  6.  
  7. import java.util.Arrays; 
  8. import java.util.List; 
  9. import java.util.concurrent.Future; 
  10. import java.util.concurrent.TimeUnit; 
  11.  
  12. @Async 
  13. @Component 
  14. public class GoodsService { 
  15.     //模擬獲取商品基本信息,內部耗時500毫秒 
  16.     public Future<String> getGoodsInfo(long goodsId) throws InterruptedException { 
  17.         TimeUnit.MILLISECONDS.sleep(500); 
  18.         return AsyncResult.forValue(String.format("商品%s基本信息!", goodsId)); 
  19.     } 
  20.  
  21.     //模擬獲取商品描述信息,內部耗時500毫秒 
  22.     public Future<String> getGoodsDesc(long goodsId) throws InterruptedException { 
  23.         TimeUnit.MILLISECONDS.sleep(500); 
  24.         return AsyncResult.forValue(String.format("商品%s描述信息!", goodsId)); 
  25.     } 
  26.  
  27.     //模擬獲取商品評論信息列表,內部耗時500毫秒 
  28.     public Future<List<String>> getGoodsComments(long goodsId) throws InterruptedException { 
  29.         TimeUnit.MILLISECONDS.sleep(500); 
  30.         List<String> comments = Arrays.asList("評論1""評論2"); 
  31.         return AsyncResult.forValue(comments); 
  32.     } 

來個spring配置類,需要加上@EnableAsync開啟bean方法的異步調用.

  1. package com.javacode2018.async.demo2; 
  2.  
  3. import org.springframework.context.annotation.ComponentScan; 
  4. import org.springframework.scheduling.annotation.EnableAsync; 
  5.  
  6. @ComponentScan 
  7. @EnableAsync 
  8. public class MainConfig2 { 

測試代碼

  1. @Test 
  2. public void test2() throws InterruptedException, ExecutionException { 
  3.     AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); 
  4.     context.register(MainConfig2.class); 
  5.     context.refresh(); 
  6.     GoodsService goodsService = context.getBean(GoodsService.class); 
  7.  
  8.     long starTime = System.currentTimeMillis(); 
  9.     System.out.println("開始獲取商品的各種信息"); 
  10.  
  11.     long goodsId = 1L; 
  12.     Future<String> goodsInfoFuture = goodsService.getGoodsInfo(goodsId); 
  13.     Future<String> goodsDescFuture = goodsService.getGoodsDesc(goodsId); 
  14.     Future<List<String>> goodsCommentsFuture = goodsService.getGoodsComments(goodsId); 
  15.  
  16.     System.out.println(goodsInfoFuture.get()); 
  17.     System.out.println(goodsDescFuture.get()); 
  18.     System.out.println(goodsCommentsFuture.get()); 
  19.  
  20.     System.out.println("商品信息獲取完畢,總耗時(ms):" + (System.currentTimeMillis() - starTime)); 
  21.  
  22.     //休眠一下,防止@Test退出 
  23.     TimeUnit.SECONDS.sleep(3); 

運行輸出

  1. 開始獲取商品的各種信息 
  2. 商品1基本信息! 
  3. 商品1描述信息! 
  4. [評論1, 評論2
  5. 商品信息獲取完畢,總耗時(ms):525 

3個方法總計耗時500毫秒左右。

如果不采用異步的方式,3個方法會同步執行,耗時差不多1.5秒,來試試,將GoodsService上的@Async去掉,然后再次執行測試案例,輸出

  1. 開始獲取商品的各種信息 
  2. 商品1基本信息! 
  3. 商品1描述信息! 
  4. [評論1, 評論2
  5. 商品信息獲取完畢,總耗時(ms):1503 

這個案例大家可以借鑒一下,按照這個思路可以去優化一下你們的代碼,方法之間無關聯的可以采用異步的方式,并行去獲取,最終耗時為最長的那個方法,整體相對于同步的方式性能提升不少。

6、自定義異步執行的線程池

默認情況下,@EnableAsync使用內置的線程池來異步調用方法,不過我們也可以自定義異步執行任務的線程池。

有2種方式來自定義異步處理的線程池

方式1

在spring容器中定義一個線程池類型的bean,bean名稱必須是taskExecutor

  1. @Bean 
  2. public Executor taskExecutor() { 
  3.     ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 
  4.     executor.setCorePoolSize(10); 
  5.     executor.setMaxPoolSize(100); 
  6.     executor.setThreadNamePrefix("my-thread-"); 
  7.     return executor; 

方式2

定義一個bean,實現AsyncConfigurer接口中的getAsyncExecutor方法,這個方法需要返回自定義的線程池,案例代碼:

  1. package com.javacode2018.async.demo3; 
  2.  
  3. import com.javacode2018.async.demo1.LogService; 
  4. import org.springframework.beans.factory.annotation.Qualifier; 
  5. import org.springframework.context.annotation.Bean; 
  6. import org.springframework.lang.Nullable; 
  7. import org.springframework.scheduling.annotation.AsyncConfigurer; 
  8. import org.springframework.scheduling.annotation.EnableAsync; 
  9. import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 
  10.  
  11. import java.util.concurrent.Executor; 
  12.  
  13. @EnableAsync 
  14. public class MainConfig3 { 
  15.  
  16.     @Bean 
  17.     public LogService logService() { 
  18.         return new LogService(); 
  19.     } 
  20.  
  21.     /** 
  22.      * 定義一個AsyncConfigurer類型的bean,實現getAsyncExecutor方法,返回自定義的線程池 
  23.      * 
  24.      * @param executor 
  25.      * @return 
  26.      */ 
  27.     @Bean 
  28.     public AsyncConfigurer asyncConfigurer(@Qualifier("logExecutors") Executor executor) { 
  29.         return new AsyncConfigurer() { 
  30.             @Nullable 
  31.             @Override 
  32.             public Executor getAsyncExecutor() { 
  33.                 return executor; 
  34.             } 
  35.         }; 
  36.     } 
  37.  
  38.     /** 
  39.      * 定義一個線程池,用來異步處理日志方法調用 
  40.      * 
  41.      * @return 
  42.      */ 
  43.     @Bean 
  44.     public Executor logExecutors() { 
  45.         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 
  46.         executor.setCorePoolSize(10); 
  47.         executor.setMaxPoolSize(100); 
  48.         //線程名稱前綴 
  49.         executor.setThreadNamePrefix("log-thread-"); //@1 
  50.         return executor; 
  51.     } 
  52.  

@1自定義的線程池中線程名稱前綴為log-thread-,運行下面測試代碼

  1. @Test 
  2. public void test3() throws InterruptedException { 
  3.     AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); 
  4.     context.register(MainConfig3.class); 
  5.     context.refresh(); 
  6.     LogService logService = context.getBean(LogService.class); 
  7.     System.out.println(Thread.currentThread() + " logService.log start," + System.currentTimeMillis()); 
  8.     logService.log("異步執行方法!"); 
  9.     System.out.println(Thread.currentThread() + " logService.log end," + System.currentTimeMillis()); 
  10.  
  11.     //休眠一下,防止@Test退出 
  12.     TimeUnit.SECONDS.sleep(3); 

輸出

  1. Thread[main,5,main] logService.log start,1595228732914 
  2. Thread[main,5,main] logService.log end,1595228732921 
  3. Thread[log-thread-1,5,main]開始記錄日志,1595228732930 
  4. Thread[log-thread-1,5,main]日志記錄完畢,1595228734931 

最后2行日志中線程名稱是log-thread-,正是我們自定義線程池中的線程。

7、自定義異常處理

異步方法若發生了異常,我們如何獲取異常信息呢?此時可以通過自定義異常處理來解決。

異常處理分2種情況

  1. 當返回值是Future的時候,方法內部有異常的時候,異常會向外拋出,可以對Future.get采用try..catch來捕獲異常
  2. 當返回值不是Future的時候,可以自定義一個bean,實現AsyncConfigurer接口中的getAsyncUncaughtExceptionHandler方法,返回自定義的異常處理器

情況1:返回值為Future類型

用法

通過try..catch來捕獲異常,如下

  1. try { 
  2.     Future<String> future = logService.mockException(); 
  3.     System.out.println(future.get()); 
  4. catch (ExecutionException e) { 
  5.     System.out.println("捕獲 ExecutionException 異常"); 
  6.     //通過e.getCause獲取實際的異常信息 
  7.     e.getCause().printStackTrace(); 
  8. catch (InterruptedException e) { 
  9.     e.printStackTrace(); 

案例

LogService中添加一個方法,返回值為Future,內部拋出一個異常,如下:

  1. @Async 
  2. public Future<String> mockException() { 
  3.     //模擬拋出一個異常 
  4.     throw new IllegalArgumentException("參數有誤!"); 

測試代碼如下

  1. @Test 
  2. public void test5() throws InterruptedException { 
  3.     AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); 
  4.     context.register(MainConfig1.class); 
  5.     context.refresh(); 
  6.     LogService logService = context.getBean(LogService.class); 
  7.     try { 
  8.         Future<String> future = logService.mockException(); 
  9.         System.out.println(future.get()); 
  10.     } catch (ExecutionException e) { 
  11.         System.out.println("捕獲 ExecutionException 異常"); 
  12.         //通過e.getCause獲取實際的異常信息 
  13.         e.getCause().printStackTrace(); 
  14.     } catch (InterruptedException e) { 
  15.         e.printStackTrace(); 
  16.     } 
  17.     //休眠一下,防止@Test退出 
  18.     TimeUnit.SECONDS.sleep(3); 

運行輸出

  1. java.lang.IllegalArgumentException: 參數有誤! 
  2. 捕獲 ExecutionException 異常 
  3.  at com.javacode2018.async.demo1.LogService.mockException(LogService.java:23
  4.  at com.javacode2018.async.demo1.LogService$$FastClassBySpringCGLIB$$32a28430.invoke(<generated>) 
  5.  at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218

情況2:無返回值異常處理

用法

當返回值不是Future的時候,可以自定義一個bean,實現AsyncConfigurer接口中的getAsyncUncaughtExceptionHandler方法,返回自定義的異常處理器,當目標方法執行過程中拋出異常的時候,此時會自動回調AsyncUncaughtExceptionHandler#handleUncaughtException這個方法,可以在這個方法中處理異常,如下:

  1. @Bean 
  2. public AsyncConfigurer asyncConfigurer() { 
  3.     return new AsyncConfigurer() { 
  4.         @Nullable 
  5.         @Override 
  6.         public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { 
  7.             return new AsyncUncaughtExceptionHandler() { 
  8.                 @Override 
  9.                 public void handleUncaughtException(Throwable ex, Method method, Object... params) { 
  10.                     //當目標方法執行過程中拋出異常的時候,此時會自動回調這個方法,可以在這個方法中處理異常 
  11.                 } 
  12.             }; 
  13.         } 
  14.     }; 

案例

LogService中添加一個方法,內部拋出一個異常,如下:

  1. @Async 
  2. public void mockNoReturnException() { 
  3.     //模擬拋出一個異常 
  4.     throw new IllegalArgumentException("無返回值的異常!"); 

來個spring配置類,通過AsyncConfigurer來自定義異常處理器AsyncUncaughtExceptionHandler

  1. package com.javacode2018.async.demo4; 
  2.  
  3. import com.javacode2018.async.demo1.LogService; 
  4. import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; 
  5. import org.springframework.context.annotation.Bean; 
  6. import org.springframework.lang.Nullable; 
  7. import org.springframework.scheduling.annotation.AsyncConfigurer; 
  8. import org.springframework.scheduling.annotation.EnableAsync; 
  9.  
  10. import java.lang.reflect.Method; 
  11. import java.util.Arrays; 
  12.  
  13. @EnableAsync 
  14. public class MainConfig4 { 
  15.  
  16.     @Bean 
  17.     public LogService logService() { 
  18.         return new LogService(); 
  19.     } 
  20.  
  21.     @Bean 
  22.     public AsyncConfigurer asyncConfigurer() { 
  23.         return new AsyncConfigurer() { 
  24.             @Nullable 
  25.             @Override 
  26.             public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { 
  27.                 return new AsyncUncaughtExceptionHandler() { 
  28.                     @Override 
  29.                     public void handleUncaughtException(Throwable ex, Method method, Object... params) { 
  30.                         String msg = String.format("方法[%s],參數[%s],發送異常了,異常詳細信息:", method, Arrays.asList(params)); 
  31.                         System.out.println(msg); 
  32.                         ex.printStackTrace(); 
  33.                     } 
  34.                 }; 
  35.             } 
  36.         }; 
  37.     } 
  38.  

運行輸出

  1. 方法[public void com.javacode2018.async.demo1.LogService.mockNoReturnException()],參數[[]],發送異常了,異常詳細信息: 
  2. java.lang.IllegalArgumentException: 無返回值的異常! 
  3.  at com.javacode2018.async.demo1.LogService.mockNoReturnException(LogService.java:29
  4.  at com.javacode2018.async.demo1.LogService$$FastClassBySpringCGLIB$$32a28430.invoke(<generated>) 
  5.  at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218

8、線程池隔離

什么是線程池隔離?

一個系統中可能有很多業務,比如充值服務、提現服務或者其他服務,這些服務中都有一些方法需要異步執行,默認情況下他們會使用同一個線程池去執行,如果有一個業務量比較大,占用了線程池中的大量線程,此時會導致其他業務的方法無法執行,那么我們可以采用線程隔離的方式,對不同的業務使用不同的線程池,相互隔離,互不影響。

@Async注解有個value參數,用來指定線程池的bean名稱,方法運行的時候,就會采用指定的線程池來執行目標方法。

使用步驟

  1. 在spring容器中,自定義線程池相關的bean
  2. @Async("線程池bean名稱")

案例

模擬2個業務:異步充值、異步提現;2個業務都采用獨立的線程池來異步執行,互不影響。

異步充值服務
  1. package com.javacode2018.async.demo5; 
  2.  
  3. import org.springframework.scheduling.annotation.Async; 
  4. import org.springframework.stereotype.Component; 
  5.  
  6. @Component 
  7. public class RechargeService { 
  8.     //模擬異步充值 
  9.     @Async(MainConfig5.RECHARGE_EXECUTORS_BEAN_NAME) 
  10.     public void recharge() { 
  11.         System.out.println(Thread.currentThread() + "模擬異步充值"); 
  12.     } 
異步提現服務
  1. package com.javacode2018.async.demo5; 
  2.  
  3. import org.springframework.scheduling.annotation.Async; 
  4. import org.springframework.stereotype.Component; 
  5.  
  6. @Component 
  7. public class CashOutService { 
  8.     //模擬異步提現 
  9.     @Async(MainConfig5.CASHOUT_EXECUTORS_BEAN_NAME) 
  10.     public void cashOut() { 
  11.         System.out.println(Thread.currentThread() + "模擬異步提現"); 
  12.     } 
spring配置類

注意@0、@1、@2、@3、@4這幾個地方的代碼,采用線程池隔離的方式,注冊了2個線程池,分別用來處理上面的2個異步業務。

  1. package com.javacode2018.async.demo5; 
  2.  
  3. import org.springframework.context.annotation.Bean; 
  4. import org.springframework.context.annotation.ComponentScan; 
  5. import org.springframework.scheduling.annotation.EnableAsync; 
  6. import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 
  7.  
  8. import java.util.concurrent.Executor; 
  9.  
  10. @EnableAsync //@0:啟用方法異步調用 
  11. @ComponentScan 
  12. public class MainConfig5 { 
  13.  
  14.     //@1:值業務線程池bean名稱 
  15.     public static final String RECHARGE_EXECUTORS_BEAN_NAME = "rechargeExecutors"
  16.     //@2:提現業務線程池bean名稱 
  17.     public static final String CASHOUT_EXECUTORS_BEAN_NAME = "cashOutExecutors"
  18.  
  19.     /** 
  20.      * @3:充值的線程池,線程名稱以recharge-thread-開頭 
  21.      * @return 
  22.      */ 
  23.     @Bean(RECHARGE_EXECUTORS_BEAN_NAME) 
  24.     public Executor rechargeExecutors() { 
  25.         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 
  26.         executor.setCorePoolSize(10); 
  27.         executor.setMaxPoolSize(100); 
  28.         //線程名稱前綴 
  29.         executor.setThreadNamePrefix("recharge-thread-"); 
  30.         return executor; 
  31.     } 
  32.  
  33.     /** 
  34.      * @4: 充值的線程池,線程名稱以cashOut-thread-開頭 
  35.      * 
  36.      * @return 
  37.      */ 
  38.     @Bean(CASHOUT_EXECUTORS_BEAN_NAME) 
  39.     public Executor cashOutExecutors() { 
  40.         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 
  41.         executor.setCorePoolSize(10); 
  42.         executor.setMaxPoolSize(100); 
  43.         //線程名稱前綴 
  44.         executor.setThreadNamePrefix("cashOut-thread-"); 
  45.         return executor; 
  46.     } 
測試代碼
  1. @Test 
  2. public void test7() throws InterruptedException { 
  3.     AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); 
  4.     context.register(MainConfig5.class); 
  5.     context.refresh(); 
  6.  
  7.     RechargeService rechargeService = context.getBean(RechargeService.class); 
  8.     rechargeService.recharge(); 
  9.     CashOutService cashOutService = context.getBean(CashOutService.class); 
  10.     cashOutService.cashOut(); 
  11.  
  12.     //休眠一下,防止@Test退出 
  13.     TimeUnit.SECONDS.sleep(3); 
運行輸出
  1. Thread[recharge-thread-1,5,main]模擬異步充值 
  2. Thread[cashOut-thread-1,5,main]模擬異步提現 

輸出中可以看出2個業務使用的是不同的線程池執行的。

9、源碼 & 原理

內部使用aop實現的,@EnableAsync會引入一個bean后置處理器:AsyncAnnotationBeanPostProcessor,將其注冊到spring容器,這個bean后置處理器在所有bean創建過程中,判斷bean的類上是否有@Async注解或者類中是否有@Async標注的方法,如果有,會通過aop給這個bean生成代理對象,會在代理對象中添加一個切面:org.springframework.scheduling.annotation.AsyncAnnotationAdvisor,這個切面中會引入一個攔截器:AnnotationAsyncExecutionInterceptor,方法異步調用的關鍵代碼就是在這個攔截器的invoke方法中實現的,可以去看一下。

責任編輯:張燕妮 來源: 今日頭條
相關推薦

2020-03-26 12:38:15

代碼節點數據

2022-09-09 09:33:14

支付寶代碼性能

2020-07-21 15:40:55

NginxJava服務器

2024-12-13 13:58:53

2025-05-09 02:00:00

代碼接口吞吐量

2011-07-01 10:11:39

2024-07-17 08:25:44

2014-03-26 10:00:06

RailsRails性能

2024-10-29 08:21:05

2021-12-29 11:06:25

Java代碼技巧

2025-05-27 01:55:00

TypeScript開發者項目

2014-07-31 09:35:57

2013-04-01 00:16:41

飛魚星無線云無線AP

2019-09-26 08:33:51

Nginx技術Java

2018-08-23 17:45:52

2021-04-21 18:57:16

二進制存儲空間

2017-12-13 13:09:36

NginxWeb應用

2017-09-26 14:56:57

MongoDBLBS服務性能

2023-06-13 13:52:00

Java 7線程池

2018-10-07 05:27:03

Python代碼機器學習
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成人精品国产 | 二区久久 | 欧美久久久久久久久中文字幕 | 国产视频亚洲视频 | 亚洲成人国产综合 | 久久久久久综合 | 国产一级免费在线观看 | 男人电影天堂 | 九九av| 久草成人 | 网站一区二区三区 | 国产精品一码二码三码在线 | 欧美日日日日bbbbb视频 | 亚洲欧美激情国产综合久久久 | 伊人久久精品 | 久久久久久av | 欧美一级黄色网 | 欧美性网 | 九九九国产| 欧美黑人一级爽快片淫片高清 | 精精久久 | 国产亚洲精品精品国产亚洲综合 | 欧美成视频| 欧美一级片在线看 | 亚洲最色网站 | 久久精品无码一区二区三区 | 三极网站 | 亚洲精品第一 | 精品国产一区二区 | 亚洲免费在线观看av | 午夜在线小视频 | 久久福利电影 | 精品欧美一区二区三区久久久小说 | caoporn视频在线 | 亚洲精品毛片av | 亚洲第一中文字幕 | 一级片av| 欧美偷偷操 | 欧美1区2区 | 欧美精品网 | 九一在线观看 |