前言
SpringBoot 想必大家都用过,但是大家平时使用发布的接口大都是同步的,那么你知道如何优雅的实现异步呢?
这篇文章就是关于如何在Spring Boot
中实现异步行为的。但首先,让我们看看同步和异步之间的区别。
在Spring Boot
中,我们可以使用@Async
注解来实现异步行为。
实现步骤
定义一个异步服务接口AsyncService.java
public interface AsyncService {
void asyncMethod() throws InterruptedException;
Future<String> futureMethod() throws InterruptedException;
}
复制代码
实现定义的接口AsyncServiceImpl.java
@Service
@Slf4j
public class AsyncServiceImpl implements AsyncService {
@Async
@Override
public void asyncMethod() throws InterruptedException {
Thread.sleep(3000);
log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName());
}
@Async
@Override
public Future<String> futureMethod() throws InterruptedException {
Thread.sleep(5000);
log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName());
return new AsyncResult<>("task Done");
}
}
复制代码
定义一个控制器AsyncController.java
@EnableAsync
@RestController
@Slf4j
public class AsyncController {
@Autowired
AsyncService asyncService;
@GetMapping("/async")
public String asyncCallerMethod() throws InterruptedException {
long start = System.currentTimeMillis();
log.info("call async method, thread name: [{}]", Thread.currentThread().getName());
asyncService.asyncMethod();
String response = "task completes in :" +
(System.currentTimeMillis() - start) + "milliseconds";
return response;
}
@GetMapping("/asyncFuture")
public String asyncFuture() throws InterruptedException, ExecutionException {
long start = System.currentTimeMillis();
log.info("call async method, thread name: [{}]", Thread.currentThread().getName());
Future<String> future = asyncService.futureMethod();
// 阻塞获取结果
String taskResult = future.get();
String response = taskResult + "task completes in :" +
(System.currentTimeMillis() - start) + "milliseconds";
return response;
}
}
复制代码
运行一下
现在我们运行一下看看,是不是异步返回的。
自定义异步任务执行器和异常处理
我们现在看看如果异常方法中报错了会怎么样?修改异步代码如下所示,会抛运行时异常:
再次执行异步接口,如下所示,会使用默认的线程池和异常处理。
我们也可以自定义异步方法的处理异常和异步任务执行器,我们需要配置 AsyncUncaughtExceptionHandler
,如下代码所示:
@Configuration
public class AsynConfiguration extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new
ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(4);
executor.setThreadNamePrefix("asyn-task-thread-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler
getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable ex,
Method method, Object... params) {
System.out.println("Exception: " + ex.getMessage());
System.out.println("Method Name: " + method.getName());
ex.printStackTrace();
}
};
}
}
复制代码
再次运行,得到的结果如下:
@Async 如何工作的?
必须通过使用 @EnableAsync
注解注解主应用程序类或任何直接或间接异步方法调用程序类来启用异步支持。主要通过代理模式实现,默认模式是 Proxy
,另一种是 AspectJ
。代理模式只允许通过代理拦截调用。永远不要从定义它的同一个类调用异步方法,它不会起作用。
当使用 @Async
对方法进行注解时,它会根据“proxyTargetClass
”属性为该对象创建一个代理。当 spring
执行这个方法时,默认情况下它会搜索关联的线程池定义。上下文中唯一的 spring
框架 TaskExecutor bean
或名为“taskExecutor
”的 Executor bean
。如果这两者都不可解析,默认会使用 spring 框架SimpleAsyncTaskExecutor
来处理异步方法的执行。
总结
在本文中,我们演示了在 spring boot 中如何使用 @Async
注解和异步方法中的异常处理实现异步行为。我们可以在一个接口中,需要访问不同的资源,比如异步调用各个其他服务的接口,可以使用@Async
,然后将结果通过Future
的方式阻塞汇总,不失为一个提高性能的好方法。
欢迎关注个人公众号【JAVA 旭阳】交流沟通
评论