Spring задача планирования - выполняется только один раз

Возможно ли запланировать метод службы Spring только один раз в точно указанное время? Например, текущее время составляет 2 вечера, но когда я нажимаю кнопку действия, я хочу, чтобы мой метод обслуживания начинался с 8 вечера. Я знаком с аннотацией @Scheduled, и я не уверен, как писать выражение cron, чтобы оно не запускалось периодически. Этот @Scheduled(cron = "0 0 20 * * ?") срабатывает каждый день в 8 вечера.
Любые предложения?

Ответ 1

Вы можете использовать одну из реализаций Spring TaskScheduler. Ниже приведен пример, в котором не требуется слишком много настроек (ConcurrentTaskScheduler, который включает однопоточный запланированный исполнитель).

Самый простой метод - это расписание с именем Runnable. и дата только. Это заставит задачу запускаться один раз после указанное время. Все остальные методы способны планировать задачи запускаются повторно.

Узнайте больше о выполнении задач & планирование

Простой рабочий пример:

private TaskScheduler scheduler;

Runnable exampleRunnable = new Runnable(){
    @Override
    public void run() {
        System.out.println("Works");
    }
};

@Async
public void executeTaskT() {
    ScheduledExecutorService localExecutor = Executors.newSingleThreadScheduledExecutor();
    scheduler = new ConcurrentTaskScheduler(localExecutor);

    scheduler.schedule(exampleRunnable,
            new Date(1432152000000L));//today at 8 pm UTC - replace it with any timestamp in miliseconds to text
}

...

executeTaskT() //call it somewhere after the spring application has been configured

Примечание:

Чтобы включить поддержку аннотаций @Scheduled и @Async, добавьте @EnableScheduling и @EnableAsync для одного из ваших @Configuration классы


Обновление - отмена запланированного задания

Метод расписания TaskScheduler возвращает ScheduledFuture, которое является отложенным действием, которое может быть отменено.

Таким образом, чтобы отменить его, вам нужно сохранить дескриптор запланированной задачи (то есть сохранить объект возврата ScheduledFuture).

Изменения в приведенном выше коде для отмены задачи:

  1. Объявите ScheduledFuture вне вашего метода executeTaskT.
    private ScheduledFuture scheduledFuture;
  1. Измените ваш вызов на расписание, чтобы сохранить возвращаемый объект как таковой:
    scheduledFuture = scheduler.schedule(exampleRunnable,
                    new Date(1432152000000L));
  1. Вызовите отменить для объекта запланированного будущего где-нибудь в вашем коде
    boolean mayInterruptIfRunning = true;
    scheduledFuture.cancel(mayInterruptIfRunning);

Ответ 2

Решение для мертвецов:

@Scheduled (initialDelay = 1000 * 30, fixedDelay = Long.MAX_VALUE)

Вы будете мертвы, прежде чем он снова выстрелит.

Ответ 3

Чтобы не создавать ScheduledExecutorService и ConcurrentTaskScheduler при каждом вызове метода, удобно инициализировать TaskScheduler при создании службы, например,

private final TaskScheduler taskScheduler = 
              new ConcurrentTaskScheduler(Executors.newScheduledThreadPool(10));

@Async не имеет смысла, так как мы просто планируем задачу и выходим из метода.

Ответ 4

Вы можете расширить PeriodicTrigger следующим образом - он проверяет lastCompletionTime: он будет нулевым, если задача никогда ранее не выполнялась. Вы можете попробовать эту вариацию, если хотите запустить задачу только один раз в определенный момент времени.

class RunOnceTrigger extends PeriodicTrigger {
    public RunOnceTrigger(long period) {
        super(period);
        setInitialDelay(period);
    }

    @Override
    public Date nextExecutionTime(TriggerContext triggerContext) {
        if(triggerContext.lastCompletionTime() == null) {   // hasn't executed yet
            return super.nextExecutionTime(triggerContext);
        }
        return null;
    }
}