Contactez-nous

Configuration et lancement de jobs Batch

Apprenez à configurer des Jobs et Steps Spring Batch via Java Config et à lancer vos traitements par lots (startup, REST, schedule) avec JobLauncher et JobParameters.

Introduction à la configuration des jobs Spring Batch

Spring Batch fournit un cadre robuste pour développer des applications de traitement par lots (batch). La configuration de ces traitements, appelés "Jobs", et de leurs étapes ("Steps") est une partie essentielle du développement. Avec Spring Boot, cette configuration se fait très naturellement en utilisant la configuration Java via des beans Spring.

L'annotation clé pour démarrer est @EnableBatchProcessing. Ajoutée à une classe @Configuration, elle active les fonctionnalités de Spring Batch et met en place une infrastructure de base, notamment un JobRepository (pour stocker l'état des jobs), un JobLauncher (pour lancer les jobs), et d'autres composants essentiels. Spring Boot auto-configure également une source de données (DataSource) pour le JobRepository si une base de données est configurée dans le projet, car Spring Batch a besoin de persister les métadonnées des exécutions de jobs.

La configuration d'un job implique typiquement de définir le Job lui-même comme un bean, ainsi que chaque Step qui le compose. Les Steps, à leur tour, sont configurés avec leurs composants spécifiques : ItemReader, ItemProcessor (optionnel), et ItemWriter pour les steps orientés chunk, ou un Tasklet pour les steps plus simples.

import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableBatchProcessing // Active Spring Batch et configure l'infrastructure de base
public class BatchConfiguration {
    // Les définitions de beans pour les Jobs, Steps, Readers, Writers, etc. iront ici
}

Définition d'un Job et de ses Steps en Java Config

Un Job Spring Batch est défini comme un bean Spring, généralement en utilisant JobBuilderFactory (bien que les API plus récentes encouragent l'utilisation de JobBuilder obtenu via le JobRepository). Un Job est essentiellement une séquence ordonnée d'une ou plusieurs Steps.

Chaque Step est également défini comme un bean, en utilisant StepBuilderFactory (ou StepBuilder via le JobRepository et PlatformTransactionManager). Les steps les plus courants sont les "chunk-oriented steps", optimisés pour le traitement d'éléments par lots (chunks). Ils nécessitent un ItemReader, un ItemProcessor (optionnel) et un ItemWriter.

Voici une structure typique de configuration Java :

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration // Assurez-vous que @EnableBatchProcessing est aussi présent
public class MonJobConfig {

    // Définition du Step
    @Bean
    public Step monPremierStep(JobRepository jobRepository, 
                                PlatformTransactionManager transactionManager, 
                                ItemReader reader, 
                                ItemProcessor processor, 
                                ItemWriter writer) {
        return new StepBuilder("monPremierStep", jobRepository)
                .chunk(10, transactionManager) // Taille du chunk = 10
                .reader(reader)
                .processor(processor)
                .writer(writer)
                .build();
    }

    // Définition du Job qui utilise le Step
    @Bean
    public Job monPremierJob(JobRepository jobRepository, Step monPremierStep) {
        return new JobBuilder("monPremierJob", jobRepository)
                .start(monPremierStep)
                // .next(unAutreStep) // Enchaîner d'autres steps si nécessaire
                // .incrementer(new RunIdIncrementer()) // Important pour relancer avec de nouveaux paramètres
                .build();
    }
    
    // Les beans pour reader, processor, writer doivent être définis ailleurs
    // @Bean ItemReader reader() { ... }
    // @Bean ItemProcessor processor() { ... }
    // @Bean ItemWriter writer() { ... }
}

Notez l'injection du JobRepository et du PlatformTransactionManager (auto-configurés par Spring Boot avec @EnableBatchProcessing et une DataSource) et des composants du step (Reader, Processor, Writer) qui doivent eux-mêmes être définis comme des beans Spring.

Le JobLauncher et les JobParameters

Pour démarrer l'exécution d'un Job, on utilise l'interface org.springframework.batch.core.launch.JobLauncher. Spring Boot auto-configure un bean JobLauncher (typiquement un SimpleJobLauncher) que vous pouvez injecter dans vos composants.

La méthode clé du JobLauncher est run(Job job, JobParameters jobParameters). Elle prend deux arguments :

  • Le bean Job que vous souhaitez lancer.
  • Un objet JobParameters.

Les JobParameters sont cruciaux dans Spring Batch. Ils représentent un ensemble de paramètres d'identification pour une exécution spécifique d'un Job (une "Job Instance"). Spring Batch utilise ces paramètres pour distinguer les différentes exécutions d'un même Job. Par exemple, si vous lancez un job d'import de fichier, le nom du fichier pourrait être un JobParameter. Si vous lancez le job une deuxième fois avec le même nom de fichier, Spring Batch (par défaut) considérera que c'est la même Job Instance et refusera de la relancer si elle s'est déjà terminée avec succès. Pour relancer le job, vous devez fournir des JobParameters différents.

Il est très courant d'ajouter un paramètre qui change à chaque lancement, comme un timestamp ou un ID unique, pour s'assurer que chaque exécution est considérée comme une nouvelle Job Instance. Spring Batch fournit l'incrémenteur RunIdIncrementer qui peut être ajouté à la définition du Job pour faciliter cela.

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class LanceurDeJob {

    @Autowired
    private JobLauncher jobLauncher;

    @Autowired
    private Job monPremierJob; // Injection du Job défini précédemment

    public void lancerJob() throws Exception {
        // Créer des paramètres uniques pour ce lancement
        JobParameters jobParameters = new JobParametersBuilder()
                .addLong("time", System.currentTimeMillis()) // Ajoute un timestamp pour l'unicité
                // .addString("inputFile", "data.csv") // Ajouter d'autres paramètres si nécessaire
                .toJobParameters();

        System.out.println("Lancement du job...");
        jobLauncher.run(monPremierJob, jobParameters);
        System.out.println("Demande de lancement effectuée."); // Note: run est souvent asynchrone
    }
}

Stratégies de lancement des jobs

Une fois votre Job configuré et le JobLauncher disponible, vous pouvez déclencher son exécution de différentes manières :

  • Au démarrage de l'application : En utilisant un CommandLineRunner ou un ApplicationRunner. C'est utile pour les jobs qui doivent s'exécuter une seule fois lorsque l'application démarre. Injectez le JobLauncher et le Job dans votre Runner et appelez la méthode run. Attention, cela peut bloquer le démarrage de l'application si le job est long, sauf si le JobLauncher est configuré pour être asynchrone.
    @Component
    public class JobRunner implements CommandLineRunner {
        @Autowired private JobLauncher jobLauncher;
        @Autowired private Job monJob;
        @Override
        public void run(String... args) throws Exception {
            JobParameters params = new JobParametersBuilder().addLong("startAt", System.currentTimeMillis()).toJobParameters();
            jobLauncher.run(monJob, params);
        }
    }
  • Via un endpoint REST : Exposez un endpoint dans un @RestController qui, lorsqu'il est appelé, injecte le JobLauncher et le Job et déclenche une nouvelle exécution avec des paramètres uniques (potentiellement dérivés de la requête HTTP). C'est utile pour un déclenchement manuel ou piloté par un système externe.
    @RestController
    public class JobController {
        @Autowired private JobLauncher jobLauncher;
        @Autowired private Job monJob;
        @PostMapping("/lancer-job")
        public ResponseEntity lancer() throws Exception {
             JobParameters params = new JobParametersBuilder().addLong("triggerTime", System.currentTimeMillis()).toJobParameters();
             jobLauncher.run(monJob, params);
             return ResponseEntity.ok("Job lancé !");
        }
    }
  • Via une tâche planifiée (@Scheduled) : Créez un service annoté avec @Scheduled qui injecte JobLauncher et Job. La méthode planifiée appellera périodiquement jobLauncher.run() avec des paramètres uniques à chaque fois (par exemple, un timestamp). C'est l'approche standard pour les jobs batch récurrents.
    @Component
    public class JobScheduler {
        @Autowired private JobLauncher jobLauncher;
        @Autowired private Job monJob;
        @Scheduled(cron = "0 0 2 * * ?") // Tous les jours à 2h du matin
        public void scheduleJob() throws Exception {
             JobParameters params = new JobParametersBuilder().addDate("runDate", new java.util.Date()).toJobParameters();
             System.out.println("Planification du lancement du job avec paramètres: " + params);
             jobLauncher.run(monJob, params);
        }
    }

Le choix de la stratégie dépend du cas d'usage du job : exécution unique au démarrage, déclenchement à la demande, ou exécution périodique automatisée. Quelle que soit la méthode, la création de JobParameters uniques pour chaque lancement est essentielle pour garantir l'idempotence et la possibilité de redémarrer les jobs ayant échoué.