Dans les applications modernes, la gestion de tâches planifiées est une nécessité : génération de rapports quotidiens, suppression de données obsolètes, envoi de notifications régulières, etc.
Spring Boot fournit une annotation simple @Scheduled, mais lorsque les besoins deviennent plus complexes (persistance des tâches, clustering, exécutions dynamiques), Quartz Scheduler s’impose comme une solution robuste et éprouvée.
Dans cet article, nous allons explorer comment intégrer Quartz à un projet Spring Boot, configurer un scheduler persistant, et mettre en place un exemple concret de suppression d’utilisateurs inactifs.
Installation
Pour ajouter Quart Scheduler à votre projet, rien de plus simple, il faut ajouter la dépendance suivante dans votre pom.xml :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>Configuration de Quartz
Spring Boot configure Quartz automatiquement via QuartzAutoConfiguration.
Cependant, pour un contrôle plus fin, il est utile de préciser le type de stockage dans application.properties
spring.quartz.job-store-type=jdbc
spring.quartz.jdbc.initialize-schema=alwaysjob-store-type=jdbc→ Quartz stocke les jobs/triggers en base.initialize-schema=always→ Spring Boot crée automatiquement les tables nécessaires (QRTZ_*).
Exemple pratique : suppression des utilisateurs inactifs
Le job Quartz
@Component
public class InactiveUserCleanupJob implements Job {
private final UserService userService;
public InactiveUserCleanupJob(UserService userService) {
this.userService = userService;
}
@Override
public void execute(JobExecutionContext context) {
userService.deleteInactiveUsers();
}
}InactiveUserCleanupJobimplémenteJob, ce qui en fait une tâche Quartz.- Le service
UserServiceest injecté pour appliquer la logique métier. - L’appel
execute()est le point d’entrée du job lors de son exécution planifiée.
La configuration du job et du trigger
@Configuration
public class JobSchedulerConfig {
@Bean
public JobDetail inactiveUserJobDetail() {
return JobBuilder.newJob(InactiveUserCleanupJob.class)
.withIdentity("inactiveUserJob")
.storeDurably()
.build();
}
@Bean
public Trigger inactiveUserJobTrigger(JobDetail jobDetail) {
return TriggerBuilder.newTrigger()
.forJob(jobDetail)
.withIdentity("inactiveUserTrigger")
.withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(13, 0))
.build();
}
}JobDetaildécrit le job à Quartz (classe cible, identifiant, persistance).Triggerdéfinit quand le job doit être exécuté.- Dans cet exemple, le job s’exécutera tous les jours à 13h00.
La configuration de l’usine de jobs Quartz
Quartz, par défaut, ne connaît pas Spring et ne sait pas injecter les beans.
Nous configurons donc une JobFactory qui délègue la création des jobs à Spring.
@Configuration
public class QuartzJobFactoryConfig {
@Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
}Et l’implémentation personnalisée :
public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}SpringBeanJobFactoryest étendu pour permettre l’injection Spring (@Autowired, services, repositories).- Chaque job Quartz créé passe par le
beanFactoryde Spring qui injecte ses dépendances.
La logique métier
@Service
public class UserService {
private static final Logger LOG = LoggerFactory.getLogger(UserService.class);
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Transactional
public void deleteInactiveUsers() {
int deleted = userRepository.deleteByStatus("inactive");
LOG.info("Suppression de {} utilisateurs inactifs.", deleted);
}
}UserServicecontient la logique métier pour supprimer les utilisateurs inactifs.@Transactionalgarantit que la suppression est exécutée dans une transaction sécurisée.
Gestion dynamique des tâches
Spring Boot facilite grandement le suivi et la gestion de Quartz grâce aux Actuators, un endpoint dédié quartz est disponible, permettant d’inspecter et d’interagir avec les jobs Quartz directement via HTTP.
Configuration des Actuators
Pour activer l’endpoint quartz, il faut l’ajouter explicitement dans les propriétés de configuration :
management.endpoints.web.exposure.include=quartz
management.endpoint.quartz.enabled=trueExemples de requêtes
Reprenons notre job de suppression des users inactifs inactiveUserJob
Lister les jobs
curl http://localhost:8080/actuator/quartz/jobs
Réponse :
{
"groups": {
"DEFAULT": {
"jobs": [
"inactiveUserJob"
]
}
}
}
Détails d’un job
Pour obtenir les détails du job InactiveUserJob appartenant au groupe DEFAULT :
curl http://localhost:8080/actuator/quartz/jobs/DEFAULT/inactiveUserJobRéponse :
{
"data": {
},
"triggers": [
{
"group": "DEFAULT",
"name": "inactiveUserTrigger",
"nextFireTime": "2025-08-19T11:00:00.000+00:00",
"priority": 5
}
],
"group": "DEFAULT",
"name": "inactiveUserJob",
"description": "Suppression des users inactifs",
"className": "fr.eletutour.quartz.tutorial.job.InactiveUserCleanupJob",
"durable": true,
"requestRecovery": false
}
Déclencher manuellement un job
il est également possible de déclencher manuellement un job Quartz via Actuator :
POSt http://localhost:8080/actuator/quartz/jobs/DEFAULT/inactiveUserJob
{
"state": "running"
}ce qui donnera la réponse suivante
{
"group": "DEFAULT",
"name": "inactiveUserJob",
"className": "fr.eletutour.quartz.tutorial.job.InactiveUserCleanupJob",
"triggerTime": "2025-08-18T17:47:34.057570Z"
}Quartz Scheduler vs Spring Batch
Dans un précédent article, nous avions vu comment planifier des tâches en utilisant Spring Batch
Bien que Quartz et Spring Batch soient deux briques de l’écosystème Spring, leurs objectifs diffèrent.
| Caractéristique | Quartz Scheduler | Spring Batch |
|---|---|---|
| Objectif principal | Planifier et exécuter des tâches récurrentes ou ponctuelles. | Traiter de grands volumes de données en lots (batch processing). |
| Gestion du temps | Exécution basée sur le temps (cron, intervalle, calendrier). | Exécution déclenchée à la demande, souvent par un planificateur externe (Quartz, cron Linux, etc.). |
| Exemple typique | Envoi quotidien d’emails, purge de données obsolètes, rappels planifiés. | Migration de données, génération de rapports volumineux, calculs massifs. |
| Persistance | Stocke l’état des jobs et triggers dans une base de données. | Stocke l’état des exécutions de batchs (métadonnées, état des steps, reprise après erreur). |
| Scalabilité | Support du clustering natif (plusieurs nœuds peuvent exécuter des tâches). | Support du partitionnement et du parallélisme pour traiter de gros volumes. |
| Simplicité d’usage | Très adapté pour la planification flexible et dynamique. | Plus complexe, mais idéal pour orchestrer des traitements longs et critiques. |
🔎 On peut retenir que :
- Quartz est centré sur quand exécuter une tâche.
- Spring Batch est centré sur comment exécuter un traitement de données en plusieurs étapes.
Dans un projet réel, il n’est pas rare de combiner les deux : Quartz planifie, et Spring Batch exécute le traitement.
Conclusion
Quartz Scheduler, couplé à Spring Boot, offre une solution robuste pour la gestion avancée des tâches planifiées.
Il convient particulièrement lorsque :
- les tâches doivent survivre à un redémarrage,
- l’application fonctionne en cluster,
- ou lorsque les utilisateurs doivent définir leurs propres horaires.
Pour des besoins plus simples, @Scheduled ou TaskExecutor peuvent suffire.
Pour du traitement de masse, Spring Batch est le choix adapté, éventuellement orchestré par Quartz.
Tout le code relatif à cet article est trouvable ici, si vous souhaitez approfondir le sujet et faire des tests
