Spring Boot 異步執行方法

最近遇到一個需求,就是當服務器接到請求並不須要任務執行完成才返回結果,能夠當即返回結果,讓任務異步的去執行。開始考慮是直接啓一個新的線程去執行任務或者把任務提交到一個線程池去執行,這兩種方法都是能夠的。可是 Spring 這麼強大,確定有什麼更簡單的方法,就 google 了一下,還真有呢。就是使用 @EnableAsync@Async 這兩個註解就 ok 了。

給方法加上 @Async 註解

package me.deweixu.aysncdemo.service;

public interface AsyncService {

    void asyncMethod(String arg);
}
package me.deweixu.aysncdemo.service.ipml;

import me.deweixu.aysncdemo.service.AsyncService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncServiceImpl implements AsyncService {

    @Async
    @Override
    public void asyncMethod(String arg) {
        System.out.println("arg:" + arg);
        System.out.println("=====" + Thread.currentThread().getName() + "=========");
    }
}

@EnableAsync

在啓動類或者配置類加上 @EnableAsync 註解java

package me.deweixu.aysncdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync
@SpringBootApplication
public class AysncDemoApplication {

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

測試

package me.deweixu.aysncdemo;

import me.deweixu.aysncdemo.service.AsyncService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class AysncDemoApplicationTests {

    @Autowired
    AsyncService asyncService;

    @Test
    public void testAsync() {
        System.out.println("=====" + Thread.currentThread().getName() + "=========");
        asyncService.asyncMethod("Async");
    }

}
=====main=========
2018-03-25 21:30:31.391  INFO 28742 --- [           main] .s.a.AnnotationAsyncExecutionInterceptor : No task executor bean found for async processing: no bean of type TaskExecutor and no bean named 'taskExecutor' either
arg:Async
=====SimpleAsyncTaskExecutor-1=========

從上面的結果看 asyncService.asyncMethod("Async") 確實異步執行了,它使用了一個新的線程。spring

指定 Executor

從上面執行的日誌能夠猜想到 Spring 默認使用 SimpleAsyncTaskExecutor 來異步執行任務的,能夠搜索到這個類。@Async 也能夠指定自定義的 Executor服務器

在啓動類中增長自定義的 Executor

package me.deweixu.aysncdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@EnableAsync
@SpringBootApplication
public class AysncDemoApplication {

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

    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        return new ThreadPoolTaskExecutor();
    }
}

指定 Executor

package me.deweixu.aysncdemo.service.ipml;

import me.deweixu.aysncdemo.service.AsyncService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncServiceImpl implements AsyncService {

    @Async("threadPoolTaskExecutor")
    @Override
    public void asyncMethod(String arg) {
        System.out.println("arg:" + arg);
        System.out.println("=====" + Thread.currentThread().getName() + "=========");
    }
}

這樣在異步執行任務的時候就使用 threadPoolTaskExecutor異步

設置默認的 Executor

上面提到若是 @Async 不指定 Executor 就默認使用 SimpleAsyncTaskExecutor,其實默認的 Executor 是能夠使用 AsyncConfigurer 接口來配置的async

@Configuration
public class SpringAsyncConfig implements AsyncConfigurer {
     
    @Override
    public Executor getAsyncExecutor() {
        return new ThreadPoolTaskExecutor();
    }
     
}

異常捕獲

在異步執行的方法中是可能出現異常的,咱們能夠在任務內部使用 try catch 來處理異常,當任務拋出異常時,Spring 也提供了捕獲它的方法。ide

實現 AsyncUncaughtExceptionHandler 接口測試

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);
        }
    }
     
}

實現 AsyncConfigurer 接口重寫 getAsyncUncaughtExceptionHandler 方法google

@Configuration
public class SpringAsyncConfig implements AsyncConfigurer {
     
    @Override
    public Executor getAsyncExecutor() {
        return new ThreadPoolTaskExecutor();
    }
    
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new CustomAsyncExceptionHandler();
    }

     
}

改寫 asyncMethod 方法使它拋出異常線程

@Async
    @Override
    public void asyncMethod(String arg) {
        System.out.println("arg:" + arg);
        System.out.println("=====" + Thread.currentThread().getName() + "=========");
        throw new NullPointerException();
    }

運行結果:日誌

=====main=========
arg:Async
=====threadPoolTaskExecutor-1=========
Exception message - Async NullPointerException
Method name - asyncMethod
Parameter value - Async

正確捕獲到了異常。

相關文章
相關標籤/搜索