Spring 非同步任務的建立、自定義配置和原理

willcat發表於2019-02-14

1 Spring 版本

5.1.4.RELEASE

2 基本使用

2.1 開啟對非同步任務的支援

@EnableAsync@Configuration類一起使用,如下所示,為整個Spring應用程式上下文啟用註釋驅動的非同步處理

 @Configuration
 @EnableAsync
 public class AppConfig {

 }
複製程式碼

2.2 編寫非同步任務

@Component
public class EmailService {
    @Async
    //無返回型別
    public void send(String from, String to, String subject, String text) {
        //do send
    }
    
    @Async
    //有返回型別
    public Future<String> send(String from, String to, String subject, String text) {
        System.out.println("Execute method asynchronously - "
         + Thread.currentThread().getName());
        try {
           Thread.sleep(5000);
           return new AsyncResult<String>("hello world !!!!");
        } catch (InterruptedException e) {
           //
        }
    
        return null;
    }
}
複製程式碼

當我們呼叫send()方法時,這個任務就會非同步去執行,@Async不僅可以用在方法上,還可以用在Bean類上,如果在類所有方法都是非同步的

3 自定義Executor

預設情況下,Spring將搜尋關聯的執行緒池定義:上下文中的唯一TaskExecutor bean,或者另一個名為“taskExecutor”的Executor bean。如果兩者都不可解析,則將使用SimpleAsyncTaskExecutor處理非同步方法呼叫。此外,具有void返回型別的帶註解的方法不能將任何異常傳送回撥用者。預設情況下,僅記錄下此類未捕獲的異常。

要自定義所有這些,需要實現AsyncConfigurer並提供:

  • 自定義Executor: 通過getAsyncExecutor()方法實現
  • 自定義AsyncUncaughtExceptionHandler: 通過getAsyncUncaughtExceptionHandler()來實現

注意:AsyncConfigurer配置類在應用程式上下文載入程式的早期初始化。如果你對其他bean有任何依賴,請確保儘可能地宣告它們為Lazy,以便讓它們通過其他後處理器。

@Configuration
@EnableAsync
public class AppConfig implements AsyncConfigurer {

     @Override
     public Executor getAsyncExecutor() {
         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
         executor.setCorePoolSize(7);
         executor.setMaxPoolSize(42);
         executor.setQueueCapacity(11);
         executor.setThreadNamePrefix("MyExecutor-");
         executor.setWaitForTasksToCompleteOnShutdown(true);//預設是false,即shutdown時會立即停止並終止當前正在執行任務
         executor.setRejectedExecutionHandler((r, executor1) -> {
            for(;;) {
                try {
                    executor1.getQueue().put(r);
                } catch (InterruptedException e) {
                    e.printStackTrace();

                }
                return;
            }
        });//指定被拒絕任務的處理方法,經過測試當併發量超過佇列長度時可以繼續執行,否則會丟擲 org.springframework.core.task.TaskRejectedException異常
         executor.initialize();
         return executor;
     }
    
     @Override
     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
         return new CustomAsyncExceptionHandler();//自定義未捕獲異常處理,參考 4 異常處理 小節
     }
}
複製程式碼

3.1 在方法級別自定義Executor

以上為應用級別重寫Executor,Spring還提供方法級別重寫: 開啟Async並自定義Executor:

@Configuration
@EnableAsync
public class SpringAsyncConfig {
    
   @Bean(name = "threadPoolTaskExecutor")
   public Executor threadPoolTaskExecutor() {
       return new ThreadPoolTaskExecutor();
   }
}
複製程式碼

使用自定義Executor

@Async("threadPoolTaskExecutor")
public void asyncMethodWithConfiguredExecutor() {
    System.out.println("Execute method with configured executor - "
      + Thread.currentThread().getName());
}
複製程式碼

4 異常處理

當方法返回型別是Future時,異常處理很容易 - Future.get()方法將丟擲異常。

但是,如果返回型別為void,則異常不會傳播到呼叫執行緒。因此,我們需要新增額外的配置來處理異常。

我們將通過實現AsyncUncaughtExceptionHandler介面來建立自定義非同步異常處理程式。當存在任何未捕獲的非同步異常時,將呼叫handleUncaughtException()方法:

public class CustomAsyncExceptionHandler
  implements AsyncUncaughtExceptionHandler {
 
    @Override
    public void handleUncaughtException(
      Throwable throwable, Method method, Object... obj) {
  
        System.out.println("Exception message - " + throwable.getMessage());
        System.out.println("Method name - " + method.getName());
        for (Object param : obj) {
            System.out.println("Parameter value - " + param);
        }
    }
     
}
複製程式碼

5 原理

概括來說,Spring使用的是AOP技術來實現的非同步任務,詳細原理之後再總結。

6 參考

相關文章