web-dev-qa-db-fra.com

Tâche de planification de printemps - exécutée une seule fois

Est-il possible de planifier une méthode de service Spring une seule fois à une heure précise? Par exemple, l'heure actuelle est 14 heures mais lorsque je clique sur le bouton d'action, je souhaite que ma méthode de service commence à 20 heures. Je connais bien les annotations @Scheduled et je ne sais pas comment écrire une expression cron pour ne pas l'exécuter périodiquement. Celui-ci, @Scheduled(cron = "0 0 20 * * ?"), est déclenché tous les jours à 20 heures.
Aucune suggestion?

20
shx

Vous pouvez utiliser l'une des implémentations de TaskScheduler de Spring. J'ai fourni un exemple ci-dessous avec un exemple qui ne nécessite pas trop de configuration (ConcurrentTaskScheduler qui encapsule un exécuteur planifié à un seul thread).

La méthode la plus simple est celle nommée schedule qui prend un Runnable et Date seulement. La tâche sera alors exécutée une fois aprèstemps spécifié. Toutes les autres méthodes sont capables de planifier tâches à exécuter à plusieurs reprises.

En savoir plus sur exécution et planification de tâches

Exemple de travail simple:

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

Remarque

Pour activer la prise en charge des annotations @Scheduled et @Async, ajoutez @EnableScheduling et @EnableAsync sur l'un de vos @Configuration Des classes


Update - Annulation de la tâche planifiée

La méthode schedule de TaskScheduler renvoie une ScheduledFuture qui est une action portant résultat retardée pouvant être annulée.

Donc, pour l’annuler, vous devez conserver un descripteur pour la tâche planifiée (c’est-à-dire conserver l’objet de retour ScheduledFuture).

Modifications apportées au code ci-dessus pour annuler la tâche:

  1. Déclarez le ScheduledFuture en dehors de votre méthode executeTaskT . private ScheduledFuture scheduledFuture;

  2. Modifiez votre appel à planifier pour conserver l'objet de retour en tant que tel: scheduledFuture = scheduler.schedule(exampleRunnable, new Date(1432152000000L));

  3. Appelez cancel sur l'objet scheduleFuture quelque part dans votre code boolean mayInterruptIfRunning = true; scheduledFuture.cancel(mayInterruptIfRunning);

21
Laurentiu L.

Pour ne pas créer ScheduledExecutorService et ConcurrentTaskScheduler à chaque appel de méthode, il est utile d’initialiser TaskScheduler à la création du service, par exemple. 

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

@Async n'a aucun sens car nous planifions simplement la tâche et quittions la méthode.

1
GKislin

Vous pouvez étendre PeriodicTrigger comme suit - il vérifie la dernière heure de complétion: il sera nul si la tâche n'a jamais été exécutée auparavant. Vous pouvez essayer une variante de ceci si vous voulez exécuter la tâche une seule fois à un moment donné. 

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;
    }
}
0
smishra