服务器之家

服务器之家 > 正文

详解Spring Boot 异步执行方法

时间:2021-04-16 13:06     来源/作者:Simeone_xu

最近遇到一个需求,就是当服务器接到请求并不需要任务执行完成才返回结果,可以立即返回结果,让任务异步的去执行。开始考虑是直接启一个新的线程去执行任务或者把任务提交到一个线程池去执行,这两种方法都是可以的。但是 Spring 这么强大,肯定有什么更简单的方法,就 google 了一下,还真有呢。就是使用 @EnableAsync 和 @Async 这两个注解就 ok 了。

给方法加上 @Async 注解

?
1
2
3
4
5
6
package me.deweixu.aysncdemo.service;
 
public interface AsyncService {
 
 void asyncMethod(String arg);
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 注解

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
 }
}

测试

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 接口来配置的

?
1
2
3
4
5
6
7
@Configuration
public class SpringAsyncConfig implements AsyncConfigurer { 
 @Override
 public Executor getAsyncExecutor() {
  return new ThreadPoolTaskExecutor();
 
}

异常捕获

在异步执行的方法中是可能出现异常的,我们可以在任务内部使用 try catch 来处理异常,当任务抛出异常时,Spring 也提供了捕获它的方法。

实现 AsyncUncaughtExceptionHandler 接口

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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 方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
public class SpringAsyncConfig implements AsyncConfigurer {
  
 @Override
 public Executor getAsyncExecutor() {
  return new ThreadPoolTaskExecutor();
 }
 
 @Override
 public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
  return new CustomAsyncExceptionHandler();
 }
 
  
}

改写 asyncMethod 方法使它抛出异常

?
1
2
3
4
5
6
7
@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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://segmentfault.com/a/1190000013974727

标签:

相关文章

热门资讯

2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
Intellij idea2020永久破解,亲测可用!!!
Intellij idea2020永久破解,亲测可用!!! 2020-07-29
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总 2020-11-13
返回顶部