第11章 任务调度
任务调度由三部分组成:
- 任务:需要在特定时间运行或定期运行的业务逻辑块;
- 触发器:指定任务应该执行的条件;
- 调度程序:根据来自触发器的信息执行任务;
11.2 Spring中的任务调度
在Spring应用程序中可以使用多种方法触发任务的执行。
一种方法是通过已存在于应用程序部署环境中的调度系统从外部触发作业。作业触发可以通过向Spring应用程序发送RESTful-WS
请求并让Spring的MVC控制器触发任务来完成。
另一种方法是在Spring中使用任务调度支持。Spring在任务调度方面提供了三个选项:
- 支持JDK定时器
- 与Quartz集成
- Spring自己的Spring TaskScheduler抽象
11.2.1 Spring TaskScheduler
抽象介绍
Spring的TaskScheduler
抽象主要有三个参与者:
- 任务:可以指定为任何Spring bean的方法
- Trigger接口:
org.springframework.scheduling.Trigger
- TaskScheduler接口:
org.springframework.scheduling.TaskScheduler
11.2.2 研究示例任务
使用XML配置和Java配置结合的方式配置简单任务调度:
// ----XML配置--------//
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<task:scheduler id="timeScheduler" pool-size="10"/>
<task:scheduled-tasks>
<task:scheduled ref="testSchedulerConfig" method="sayTime" fixed-delay="3000"/>
</task:scheduled-tasks>
</beans>
// ----Java配置与测试--------//
@Slf4j
@Configuration
@Component("testSchedulerConfig")
@ImportResource("classpath:chapter11/ac_task_test.xml")
public class TestSchedulerConfig {
public void sayTime() {
log.error("======" + new Date().toLocaleString());
}
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(TestSchedulerConfig.class);
}
}
11.2.3 使用注解进行任务调度
Spring提供了@Scheduled
注解来使用Spring的TaskScheduler
抽象。要使用注解支持,可以在XML配置中使用<task:annotation-driver>
标记或在配置类上使用@EnableScheduling
注解。
@Slf4j
@Configuration
@EnableScheduling
public class TestAnnoSchedulerConfig {
@Scheduled(fixedRate = 1000)
public void sayTime() {
log.error("======" + new Date().toLocaleString());
}
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(TestAnnoSchedulerConfig.class);
}
}
任务调度原理:
org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor
这个类既是后置处理器,又是事件监听器,监听ContextRefreshedEvent
事件;
在refresh()--finishBeanFactoryInitialization()
中调用后置处理器方法进行配置;在refresh()--finishRefresh();
中通过事件监听的方式开始执行任务调度;
11.2.4 Spring中异步任务的执行
Spring使用@Async
注解来异步执行任务。
// ----------定义异步任务-------------//
@Slf4j
@Service("asyncService")
public class AsyncServiceImpl implements AsyncService {
@Async
@Override
public void asyncTask() {
log.error("AsyncServiceImpl...asyncTask...111");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.error("AsyncServiceImpl...asyncTask...222");
}
@Async
@Override
public Future<String> asyncWithReturn(String name) {
log.error("AsyncServiceImpl...asyncWithReturn...111==" + name);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.error("AsyncServiceImpl...asyncWithReturn...222==" + name);
return new AsyncResult<>("Hello: " + name);
}
}
// ----------定义Java配置-------------//
@Configuration
@EnableAsync
@Import({AsyncServiceImpl.class})
public class AsyncConfig {
}
// ----------测试程序-------------//
@Slf4j
public class AsyncTaskDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AsyncConfig.class);
AsyncService asyncService = ctx.getBean("asyncService", AsyncService.class);
for (int i = 0; i < 5; i++) {
asyncService.asyncTask();
}
Future<String> result1 = asyncService.asyncWithReturn("John Mayer");
Future<String> result2 = asyncService.asyncWithReturn("Eric Clapton");
Future<String> result3 = asyncService.asyncWithReturn("BB King");
log.error("result1 == " + result1.get());
log.error("result2 == " + result2.get());
log.error("result3 == " + result3.get());
}
}
原理:基于后置处理器org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor
11.3 Spring中任务的执行
Spring通过TaskExecutor
接口执行任务的抽象。TaskExecutor执行由Java Runnable
实现表示的任务。Spring提供了很多适合不同需求的TaskExecutor实现。
SimpleAsyncTaskExecutor
:在每次调用时创建新线程,不重用现有的线程。SyncTaskExecutor
:不会异步执行,调用发生在调用线程中。SimpleThreadPoolTaskExecutor
:Quartz的SimpleThreadPool
的子类,但需要Quartz和非Quartz组件之间共享线程池时使用。ThreadPoolTaskExecutor
:TaskExecutor的一种实现,提供了通过bean属性配置java.util.concurrent.ThreadPoolExecutor
并将其作为Spring TaskExecutor公开的功能。
// --------定义执行任务----------- //
@Slf4j
@Component
public class TaskToExecute {
@Autowired
private TaskExecutor taskExecutor;
public void executeTask() {
for (int i = 0; i < 10; i++) {
taskExecutor.execute(() -> log.info("Hello from thread: " + Thread.currentThread().getName()));
}
}
}
// --------定义Java配置类----------- //
@Configuration
@EnableAsync
@Import({TaskToExecute.class})
public class TaskConfig {
@Bean
public TaskExecutor taskExecutor() {
return new SimpleAsyncTaskExecutor();
}
}
// --------测试程序----------- //
public class TaskExecutorDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(TaskConfig.class);
TaskToExecute taskToExecute = ctx.getBean(TaskToExecute.class);
taskToExecute.executeTask();
ctx.close();
}
}
通过Async注解异步执行时,默认使用的是SimpleAsyncTaskExecutor
,如果没有定义类型为TaskExecutor的bean或名称为taskExecutor
的类型为Executor的bean。