最近遇到一個需求,就是當服務器接到請求並不須要任務執行完成才返回結果,能夠當即返回結果,讓任務異步的去執行。開始考慮是直接啓一個新的線程去執行任務或者把任務提交到一個線程池去執行,這兩種方法都是能夠的。可是Spring
這麼強大,確定有什麼更簡單的方法,就@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
正確捕獲到了異常。