Spring Framework 自身提供了对异步任务的支持,本文介绍 Spring Boot 中异步任务开发。
1 定义一个服务类,包含 2 个方法,假设这 2 个方法都非常繁重和耗时。
package demo.spring.boot.async.service;
public interface HeavyService {
void heavy1()
throws InterruptedException;
void heavy2()
throws InterruptedException;
}
package demo.spring.boot.async.service.impl;
import demo.spring.boot.async.service.HeavyService;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class HeavyServiceImpl
implements HeavyService {
@Override
public void heavy1()
throws InterruptedException {
System.out.println("heavy1 started at " + LocalDateTime.now());
// 模拟方法耗时
Thread.sleep(3000);
System.out.println("heavy1 ended at " + LocalDateTime.now());
}
@Override
public void heavy2()
throws InterruptedException {
System.out.println("heavy2 started at " + LocalDateTime.now());
// 模拟方法耗时
Thread.sleep(2000);
System.out.println("heavy2 ended at " + LocalDateTime.now());
}
}
2 定义一个定时任务,每隔一段时间调用以上 2 个方法(如何定义定时任务参考:Spring Boot 定时任务 -- @Scheduled)
package demo.spring.boot.async.scheduled;
import demo.spring.boot.async.service.HeavyService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class ScheduledTask {
private final HeavyService heavyService;
public ScheduledTask(HeavyService heavyService) {
this.heavyService = heavyService;
}
@Scheduled(fixedRate = 10000)
public void invoke()
throws InterruptedException {
System.out.println("invoke started at " + LocalDateTime.now());
heavyService.heavy1();
heavyService.heavy2();
}
}
3 不配置异步任务时执行过程日志如下,从日志中可以看出,heavy1
和 heavy2
这 2 个方法是串行执行的,每次执行完这 2 个任务需耗时 5 秒多。
2018-08-03 21:45:04.108 INFO 18932 --- [ main] d.s.b.a.DemoSpringBootAsyncApplication : Starting DemoSpringBootAsyncApplication on LAPTOP-C375ASPB with PID 18932 (D:\JYL\DEV\IdeaProjects\demo\demo-spring-boot-async\target\classes started by Ji in D:\JYL\DEV\IdeaProjects\demo)
2018-08-03 21:45:04.112 INFO 18932 --- [ main] d.s.b.a.DemoSpringBootAsyncApplication : No active profile set, falling back to default profiles: default
2018-08-03 21:45:04.158 INFO 18932 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@53ca01a2: startup date [Fri Aug 03 21:45:04 CST 2018]; root of context hierarchy
2018-08-03 21:45:04.799 INFO 18932 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-08-03 21:45:04.810 INFO 18932 --- [ main] s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
2018-08-03 21:45:04.819 INFO 18932 --- [ main] d.s.b.a.DemoSpringBootAsyncApplication : Started DemoSpringBootAsyncApplication in 0.994 seconds (JVM running for 1.813)
invoke started at 2018-08-03T21:45:04.823
heavy1 started at 2018-08-03T21:45:04.823
heavy1 ended at 2018-08-03T21:45:07.826
heavy2 started at 2018-08-03T21:45:07.826
heavy2 ended at 2018-08-03T21:45:09.835
invoke started at 2018-08-03T21:45:14.823
heavy1 started at 2018-08-03T21:45:14.823
heavy1 ended at 2018-08-03T21:45:17.823
heavy2 started at 2018-08-03T21:45:17.823
heavy2 ended at 2018-08-03T21:45:19.844
......
4 添加异步任务支持
4.1 定义异步配置类
package demo.spring.boot.async.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
@Configuration
@EnableAsync
public class AsyncConfig {
}
4.2 修改 heavy1
和 heavy2
方法,添加 @Async
注释
package demo.spring.boot.async.service.impl;
import demo.spring.boot.async.service.HeavyService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class HeavyServiceImpl
implements HeavyService {
@Override
@Async
public void heavy1()
throws InterruptedException {
System.out.println("heavy1 started at " + LocalDateTime.now());
// 模拟方法耗时
Thread.sleep(3000);
System.out.println("heavy1 ended at " + LocalDateTime.now());
}
@Override
@Async
public void heavy2()
throws InterruptedException {
System.out.println("heavy2 started at " + LocalDateTime.now());
// 模拟方法耗时
Thread.sleep(2000);
System.out.println("heavy2 ended at " + LocalDateTime.now());
}
}
4.3 再次启动工程运行定时任务,从日志中可以看出,heavy1
和 heavy2
这 2 个方法是并行执行的,执行完这 2 个任务只耗时 3 秒多,相当于 heavy1
耗时。
2018-08-03 21:47:18.143 INFO 21052 --- [ main] d.s.b.a.DemoSpringBootAsyncApplication : Starting DemoSpringBootAsyncApplication on LAPTOP-C375ASPB with PID 21052 (D:\JYL\DEV\IdeaProjects\demo\demo-spring-boot-async\target\classes started by Ji in D:\JYL\DEV\IdeaProjects\demo)
2018-08-03 21:47:18.146 INFO 21052 --- [ main] d.s.b.a.DemoSpringBootAsyncApplication : No active profile set, falling back to default profiles: default
2018-08-03 21:47:18.191 INFO 21052 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@548a102f: startup date [Fri Aug 03 21:47:18 CST 2018]; root of context hierarchy
2018-08-03 21:47:18.847 INFO 21052 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-08-03 21:47:18.859 INFO 21052 --- [ main] s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
2018-08-03 21:47:18.867 INFO 21052 --- [ main] d.s.b.a.DemoSpringBootAsyncApplication : Started DemoSpringBootAsyncApplication in 0.984 seconds (JVM running for 1.708)
invoke started at 2018-08-03T21:47:18.867
2018-08-03 21:47:18.869 INFO 21052 --- [pool-1-thread-1] .s.a.AnnotationAsyncExecutionInterceptor : No task executor bean found for async processing: no bean of type TaskExecutor and no bean named 'taskExecutor' either
heavy1 started at 2018-08-03T21:47:18.876
heavy2 started at 2018-08-03T21:47:18.876
heavy2 ended at 2018-08-03T21:47:20.883
heavy1 ended at 2018-08-03T21:47:21.880
invoke started at 2018-08-03T21:47:28.876
heavy1 started at 2018-08-03T21:47:28.877
heavy2 started at 2018-08-03T21:47:28.877
heavy2 ended at 2018-08-03T21:47:30.888
heavy1 ended at 2018-08-03T21:47:31.889
......
@Async
属性
附
工程目录结构
POM
4.0.0
demo.spring.boot
demo-spring-boot-async
0.0.1-SNAPSHOT
jar
demo-spring-boot-async
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
2.0.4.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin