前言:
最近的時候遇到一個需求,就是當服務器接到請求並不需要任務執行完成才返回結果,可以立即返回結果,讓任務異步的去執行。開始考慮是直接啟一個新的線程去執行任務或者把任務提交到一個線程池去執行,這兩種方法都是可以的。但是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 注解
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") 確實異步執行了,它使用了一個新的線程。
指定 "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 接口來配置的
@Configuration
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
return new ThreadPoolTaskExecutor();
}
}
異常捕獲
在異步執行的方法中是可能出現異常的,我們可以在任務內部使用 try catch 來處理異常,當任務拋出異常時, Spring 也提供了捕獲它的方法。
實現 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 方法
@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
正確捕獲到了異常。
我是小架,我們
下篇文章見!