Programmation au printemps avec Quartz

Planification au printemps avec Quartz

1. Vue d'ensemble

Dans ce didacticiel, nous allons créer un simpleScheduler in Spring with Quartz.

Nous commencerons par un objectif simple: configurer facilement une nouvelle tâche planifiée.

1.1. Composants clés de l'API Quartz

Quartz a une architecture modulaire. Il se compose de plusieurs composants de base pouvant être combinés selon les besoins. Dans ce didacticiel, nous nous concentrerons sur ceux qui sont communs à chaque tâche:Job,JobDetail,Trigger etScheduler.

Bien que nous utilisions Spring pour gérer l'application, chaque composant individuel peut être configuré de deux manières: la manièreQuartz ou la manièreSpring (en utilisant ses classes de commodité).

Nous allons couvrir les deux autant que possible par souci d’exhaustivité, mais l’un ou l’autre peut être adopté. Commençons à construire, un composant à la fois.

Lectures complémentaires:

Guide du planificateur de tâches de printemps

Un guide rapide et pratique sur la planification au printemps avec le planificateur de tâches

Read more

Planification dans Java EE

Démonstration de la planification de tâches dans Java EE à l'aide de l'annotation @Schedule et du service de minuteur.

Read more

Introduction à Drools

Apprenez à utiliser Drools en tant que système de gestion des règles métier (BRMS).

Read more

2. Job etJobDetail

2.1. Job

L'API fournit une interfaceJob ayant une seule méthode -execute. Elle doit être implémentée par la classe qui contient le travail réel à effectuer, c'est-à-dire la tâche. Lorsqu'un déclencheur de tâche se déclenche, le planificateur appelle la méthodeexecute, en lui passant un objetJobExecutionContext.

LeJobExecutionContext fournit à l'instance de travail des informations sur son environnement d'exécution, y compris un handle vers le planificateur, un handle vers le déclencheur et l'objetJobDetail du travail.

Dans cet exemple rapide, le travail délègue la tâche à une classe de service: __

@Component
public class SampleJob implements Job {

    @Autowired
    private SampleJobService jobService;

    public void execute(JobExecutionContext context) throws JobExecutionException {
        jobService.executeSampleJob();
    }
}

2.2. JobDetail

Bien que le travail soit le cheval de bataille, Quartz ne stocke pas une instance réelle de la classe de travail. Au lieu de cela, nous pouvons définir une instance desJob en utilisant la classeJobDetail. La classe du travail doit être fournie auxJobDetail pour qu’ils connaissent lestype du travail à exécuter.

2.3. QuartzJobBuilder

QuartzJobBuilder fournit une API de type constructeur pour construire des entitésJobDetail.

@Bean
public JobDetail jobDetail() {
    return JobBuilder.newJob().ofType(SampleJob.class)
      .storeDurably()
      .withIdentity("Qrtz_Job_Detail")
      .withDescription("Invoke Sample Job service...")
      .build();
}

2.4. PrintempsJobDetailFactoryBean

LesJobDetailFactoryBean de Spring fournissent une utilisation de style bean pour la configuration des instances deJobDetail. Il utilise le nom du bean Spring comme nom de travail, sauf indication contraire:

@Bean
public JobDetailFactoryBean jobDetail() {
    JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean();
    jobDetailFactory.setJobClass(SampleJob.class);
    jobDetailFactory.setDescription("Invoke Sample Job service...");
    jobDetailFactory.setDurability(true);
    return jobDetailFactory;
}

Une nouvelle instance deJobDetail est créée pour chaque exécution du travail. L'objetJobDetail transmet les propriétés détaillées du travail. Une fois l'exécution terminée, les références à l'instance sont supprimées.

3. Déclencheur

UnTrigger est le mécanisme pour programmer unJob, c'est-à-dire une instance deTrigger «déclenche» l'exécution d'un travail. Il existe une séparation claire des responsabilités entre lesJob (notion de tâche) et lesTrigger (mécanisme de planification).

En plus deJob, le déclencheur a également besoin d'untype qui peut être choisi en fonction des exigences de planification.

Disons que nous voulons planifier notre tâche pour exécuteronce every hour, indéfiniment - nous pouvons utiliser lesTriggerBuilder de Quartz ou lesSimpleTriggerFactoryBeande Spring pour le faire.

3.1. QuartzTriggerBuilder

TriggerBuilder est une API de type constructeur pour construire l'entitéTrigger:

@Bean
public Trigger trigger(JobDetail job) {
    return TriggerBuilder.newTrigger().forJob(job)
      .withIdentity("Qrtz_Trigger")
      .withDescription("Sample trigger")
      .withSchedule(simpleSchedule().repeatForever().withIntervalInHours(1))
      .build();
}

3.2. PrintempsSimpleTriggerFactoryBean

SimpleTriggerFactoryBean fournit une utilisation de style bean pour la configuration deSimpleTrigger. Il utilise le nom du bean Spring comme nom de déclencheur et utilise par défaut la répétition indéfinie, sauf indication contraire:

@Bean
public SimpleTriggerFactoryBean trigger(JobDetail job) {
    SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean();
    trigger.setJobDetail(job);
    trigger.setRepeatInterval(3600000);
    trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
    return trigger;
}

4. Configuration desJobStore

JobStore fournit le mécanisme de stockage pour lesJob etTrigger, et est responsable de la maintenance de toutes les données pertinentes pour le planificateur de travaux. L'API prend en charge les magasinsin-memory etpersistent.

4.1. En mémoireJobStore

À titre d'exemple, nous utiliserons leRAMJobStore en mémoire qui offre des performances ultra-rapides et une configuration simple viaquartz.properties:

org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore

L'inconvénient évident duRAMJobStore est qu'il est de naturevolatile. Toutes les informations de planification sont perdues entre les arrêts. Si les définitions de travail et les planifications doivent être conservées entre les arrêts, lesJDBCJobStorepersistants doivent être utilisés à la place. Pour activer unJobStore en mémoire dans Spring,, nous définissons cette propriété dans nosapplication.properties:

spring.quartz.job-store-type=memory

4.2. JDBCJobStore

Il existe deux types deJDBCJobStore:JobStoreTX etJobStoreCMT. Ils font tous deux le même travail de stockage des informations de planification dans une base de données.

La différence entre les deux réside dans la manière dont ils gèrent les transactions qui valident les données. Le typeJobStoreCMT nécessite une transaction d'application pour stocker les données, tandis que le typeJobStoreTX démarre et gère ses propres transactions.

Il existe plusieurs propriétés à définir pour unJDBCJobStore. Au minimum, nous devons spécifier le type deJDBCJobStore, la source de données et la classe du pilote de base de données. Il existe des classes de pilotes pour la plupart des bases de données, maisStdJDBCDelegate couvre la plupart des cas:

org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource=quartzDataSource

La configuration d'un JDBCJobStore dans Spring prend quelques étapes. Tout d'abord, nous définissons le type de magasin dans nosapplication.properties:

spring.quartz.job-store-type=jdbc

Ensuite, nous devons activer la configuration automatique et donner à Spring la source de données nécessaire au planificateur Quartz. L'annotation@QuartzDataSource fait le gros du travail de configuration et d'initialisation de la base de données Quartz pour nous:

@Configuration
@EnableAutoConfiguration
public class SpringQrtzScheduler {

    @Bean
    @QuartzDataSource
    public DataSource quartzDataSource() {
        return DataSourceBuilder.create().build();
    }
}

5. Scheduler

L'interfaceScheduler est la principale API d'interfaçage avec le planificateur de travaux.

UnScheduler peut être instancié avec unSchedulerFactory. Une fois créé,Jobs etTriggers peuvent être enregistrés avec lui. Initialement, leScheduler est en mode «stand-by», et sa méthodestart doit être appelée pour démarrer les threads qui déclenchent l'exécution des jobs.

5.1. QuartzStdSchedulerFactory

En invoquant simplement la méthodegetScheduler sur lesStdSchedulerFactory, nous pouvons instancier lesScheduler, l'initialiser (avec lesJobStore etThreadPool configurés), et renvoyer un handle vers son API:

@Bean
public Scheduler scheduler(Trigger trigger, JobDetail job, SchedulerFactoryBean factory)
  throws SchedulerException {
    Scheduler scheduler = factory.getScheduler();
    scheduler.scheduleJob(job, trigger);
    scheduler.start();
    return scheduler;
}

5.2. PrintempsSchedulerFactoryBean

LeSchedulerFactoryBean de Spring fournit une utilisation de style bean pour configurer unScheduler, gère son cycle de vie dans le contexte de l'application et expose lesScheduler comme un bean pour l'injection de dépendances:

@Bean
public SchedulerFactoryBean scheduler(Trigger trigger, JobDetail job, DataSource quartzDataSource) {
    SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
    schedulerFactory.setConfigLocation(new ClassPathResource("quartz.properties"));

    schedulerFactory.setJobFactory(springBeanJobFactory());
    schedulerFactory.setJobDetails(job);
    schedulerFactory.setTriggers(trigger);
    schedulerFactory.setDataSource(quartzDataSource);
    return schedulerFactory;
}

5.3. Configuration deSpringBeanJobFactory

LeSpringBeanJobFactory prend en charge l'injection du contexte du planificateur, de la mappe des données de travail et des entrées de données de déclenchement en tant que propriétés dans le bean de travail lors de la création d'une instance.

Cependant, il ne prend pas en charge l'injection de références de haricots à partir desapplication context. Grâce à l'auteur dethis blog post, nous pouvons ajouter le support deauto-wiring àSpringBeanJobFactory comme ceci:

@Bean
public SpringBeanJobFactory springBeanJobFactory() {
    AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
    jobFactory.setApplicationContext(applicationContext);
    return jobFactory;
}

6. Conclusion

C'est tout. Nous venons de créer notre premier planificateur de base en utilisant l'API Quartz ainsi que les classes de commodité de Spring.

La conclusion clé de ce tutoriel est que nous avons été en mesure de configurer un travail avec seulement quelques lignes de code et sans utiliser de configuration basée sur XML.

Lessource code complets de l'exemple sont disponibles enthis github project. C'est un projet Maven qui peut être importé et exécuté tel quel. Le paramètre par défaut utilise les classes de commodité de Spring, qui peuvent être facilement basculées vers l'API Quartz avec un paramètre d'exécution (reportez-vous au README.md dans le référentiel).