本文正在参加「金石计划」
前语
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");
}
}
-
AsyncServiceImpl
是一个spring
办理的bean
。 - 您的异步办法必须是公共的,而且是被
@Async
注解修饰。 - 回来类型被限制为
void
或Future
。
- 界说一个控制器
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;
}
}
- 要害点,需要添加启用异步的注解
@EnableAsync
,当然这个注解加在其他地方也ok得。 - 当外部调用该接口时,
asyncMethod()
将由默许使命履行程序创建的另一个线程履行,主线程不需要等候完结异步办法履行。
- 运转一下
现在咱们运转一下看看,是不是异步回来的。
- 能够看到调用
/async
接口,终究一步调用了办法。
- 调用
/asyncFuture
,发现回来5秒多,难道不是异步的吗?其实也是异步的,看日志能够看出来,只不过咱们回来的是Future
,调用Futrue.get()
是堵塞的。
自界说异步使命履行器和反常处理
咱们现在看看假如反常办法中报错了会怎么样?修正异步代码如下所示,会抛运转时反常:
再次履行异步接口,如下所示,会运用默许的线程池和反常处理。
咱们也能够自界说异步办法的处理反常和异步使命履行器,咱们需要配置 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旭阳】交流沟通