我使用Spring Framework的@Scheduled
注释创建了一个简单的计划任务。
@Scheduled(fixedRate = 2000)
public void doSomething() {}
现在我想在不再需要时停止这项任务。
我知道在这个方法的开头可以有一个替代方法来检查一个条件标志,但是这不会停止执行这个方法。
是否有任何Spring提供停止@Scheduled
任务?
提供ScheduledAnnotationBeanPostProcessor
并显式调用postProcessBeforeDestruction(Object bean, String beanName)
,用于应该停止其调度的bean。
private final Map<Object, ScheduledFuture<?>> scheduledTasks =
new IdentityHashMap<>();
@Scheduled(fixedRate = 2000)
public void fixedRateJob() {
System.out.println("Something to be done every 2 secs");
}
@Bean
public TaskScheduler poolScheduler() {
return new CustomTaskScheduler();
}
class CustomTaskScheduler extends ThreadPoolTaskScheduler {
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) {
ScheduledFuture<?> future = super.scheduleAtFixedRate(task, period);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task;
scheduledTasks.put(runnable.getTarget(), future);
return future;
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) {
ScheduledFuture<?> future = super.scheduleAtFixedRate(task, startTime, period);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task;
scheduledTasks.put(runnable.getTarget(), future);
return future;
}
}
当必须停止bean的调度时,您可以查找映射以获取相应的Future
并显式取消它。
这个问题有点含糊不清
我最好的猜测是,您希望使用可能以可恢复的方式在同一个应用程序中出现的条件来关闭任务。我会尝试根据这个假设回答。
这是我能想到的simplest possible解决方案,但是我会做一些改进,比如早期返回而不是嵌套ifs
@Component
public class SomeScheduledJob implements Job {
private static final Logger LOGGER = LoggerFactory.getLogger(SomeScheduledJob.class);
@Value("${jobs.mediafiles.imagesPurgeJob.enable}")
private boolean imagesPurgeJobEnable;
@Override
@Scheduled(cron = "${jobs.mediafiles.imagesPurgeJob.schedule}")
public void execute() {
if(!imagesPurgeJobEnable){
return;
}
Do your conditional job here...
}
上述代码的属性
jobs.mediafiles.imagesPurgeJob.enable=true or false
jobs.mediafiles.imagesPurgeJob.schedule=0 0 0/12 * * ?
前段时间我在我的项目中有这个要求,任何组件都应该能够创建一个新的计划任务或停止调度程序(所有任务)。所以我做了这样的事情
@Configuration
@EnableScheduling
@ComponentScan
@Component
public class CentralScheduler {
private static AnnotationConfigApplicationContext CONTEXT = null;
@Autowired
private ThreadPoolTaskScheduler scheduler;
public static CentralScheduler getInstance() {
if (!isValidBean()) {
CONTEXT = new AnnotationConfigApplicationContext(CentralScheduler.class);
}
return CONTEXT.getBean(CentralScheduler.class);
}
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
return new ThreadPoolTaskScheduler();
}
public void start(Runnable task, String scheduleExpression) throws Exception {
scheduler.schedule(task, new CronTrigger(scheduleExpression));
}
public void start(Runnable task, Long delay) throws Exception {
scheduler.scheduleWithFixedDelay(task, delay);
}
public void stopAll() {
scheduler.shutdown();
CONTEXT.close();
}
private static boolean isValidBean() {
if (CONTEXT == null || !CONTEXT.isActive()) {
return false;
}
try {
CONTEXT.getBean(CentralScheduler.class);
} catch (NoSuchBeanDefinitionException ex) {
return false;
}
return true;
}
}
所以我可以做的事情
Runnable task = new MyTask();
CentralScheduler.getInstance().start(task, 30_000L);
CentralScheduler.getInstance().stopAll();
请记住,出于某些原因,我不必担心并发性。否则应该有一些同步。
这是一个示例,我们可以停止,启动和列出所有计划的运行任务:
@RestController
@RequestMapping("/test")
public class TestController {
private static final String SCHEDULED_TASKS = "scheduledTasks";
@Autowired
private ScheduledAnnotationBeanPostProcessor postProcessor;
@Autowired
private ScheduledTasks scheduledTasks;
@Autowired
private ObjectMapper objectMapper;
@GetMapping(value = "/stopScheduler")
public String stopSchedule(){
postProcessor.postProcessBeforeDestruction(scheduledTasks, SCHEDULED_TASKS);
return "OK";
}
@GetMapping(value = "/startScheduler")
public String startSchedule(){
postProcessor.postProcessAfterInitialization(scheduledTasks, SCHEDULED_TASKS);
return "OK";
}
@GetMapping(value = "/listScheduler")
public String listSchedules() throws JsonProcessingException{
Set<ScheduledTask> setTasks = postProcessor.getScheduledTasks();
if(!setTasks.isEmpty()){
return objectMapper.writeValueAsString(setTasks);
}else{
return "No running tasks !";
}
}
}
当spring处理Scheduled
时,它将迭代每个注释此注释的方法并按bean组织任务,如下面的源所示:
private final Map<Object, Set<ScheduledTask>> scheduledTasks =
new IdentityHashMap<Object, Set<ScheduledTask>>(16);
如果您只想取消重复的计划任务,您可以执行以下操作(这是我的仓库中的runnable demo):
@Autowired
private ScheduledAnnotationBeanPostProcessor postProcessor;
@Autowired
private TestSchedule testSchedule;
public void later() {
postProcessor.postProcessBeforeDestruction(test, "testSchedule");
}
它会找到这个bean的ScheduledTask
并逐一取消它。应该注意的是它还将停止当前运行的方法(如postProcessBeforeDestruction
源显示)。
synchronized (this.scheduledTasks) {
tasks = this.scheduledTasks.remove(bean); // remove from future running
}
if (tasks != null) {
for (ScheduledTask task : tasks) {
task.cancel(); // cancel current running method
}
}