乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 批处理概述与 Spring Batch 核心理念

    • Spring Batch 概述
    • Job-Instance-Execution 三层生命周期
    • 三层架构
    • Chunk 处理模型
    • Tasklet 处理模型
  • 第2章 Job与作业配置

    • Job 详解
    • Job 配置
    • JobLauncher 详解
    • JobParameters 详解
    • JobRepository 详解
  • 第3章 Step与执行模型

    • Step 详解
    • Step 配置
    • ExecutionContext 详解
    • ItemStream 与状态管理
    • StepScope 与 JobScope
  • 第4章 ItemReader数据读取

    • ItemReader 详解
    • FlatFileItemReader 详解
    • JdbcItemReader 详解
    • MultiResourceItemReader 详解
  • 第5章 ItemProcessor 数据处理

    • ItemProcessor 详解
  • 第6章 ItemWriter 数据写出

    • ItemWriter 详解
    • FlatFileItemWriter 详解
    • CompositeItemWriter 详解
    • JdbcBatchItemWriter 详解
  • 第7章 Chunk 处理与事务边界

    • Chunk 处理模型详解
    • 事务边界
  • 第8章 作业参数与启动

    • 命令行与 Web 启动
  • 第9章 监听器与拦截器

    • 监听器详解
  • 第10章 重启重试与跳过策略

    • 重启与重试
    • 跳过策略
  • 第11章 作业流与条件决策

    • 作业流与条件决策
  • 第12章 分区与并行处理

    • 分区详解
    • 并行 Step 与 Split
  • 第13章 与 Spring Boot 集成实践

    • Spring Boot 集成
  • 第15章 运维与监控

    • 大作业设计模式
    • 运维与监控

JobLauncher 详解

本章定位:深入掌握 JobLauncher——作业的启动器,理解同步/异步执行、自定义配置以及多种触发方式。

定义与作用

JobLauncher 是 Spring Batch 中负责启动 Job 的核心接口。接收一个 Job 和一组 JobParameters,创建并启动 JobExecution。它是外部世界(命令行、Web 请求、定时任务)与批处理引擎之间的唯一入口。

飞翔科技架构师白歌说:

"JobLauncher 是 Spring Batch 的'启动按钮'。不管你是从命令行、REST API 还是定时任务触发,最终都是调用 jobLauncher.run(job, params)。它是所有触发方式的统一入口。"

核心原理

JobLauncher 执行流程

同步 vs 异步启动

模式行为适用场景
同步(默认)run() 阻塞直到 Job 完成,返回最终的 JobExecution命令行、简单 REST API
异步run() 立即返回,Job 在后台线程执行Web 应用不阻塞 HTTP 请求、多个 Job 并行启动
// 同步启动(默认)
JobExecution execution = jobLauncher.run(job, params);
// 这行代码会等到 Job 完全结束才返回
System.out.println("Job finished: " + execution.getStatus());

// 异步启动
@Bean
public JobLauncher asyncJobLauncher(JobRepository jobRepository) throws Exception {
    SimpleJobLauncher launcher = new SimpleJobLauncher();
    launcher.setJobRepository(jobRepository);
    launcher.setTaskExecutor(new SimpleAsyncTaskExecutor());
    launcher.afterPropertiesSet();
    return launcher;
}

完整示例

场景一:飞翔科技——REST API 触发订单处理 Job

背景:运营部杨英希望通过一个管理后台页面手动触发订单处理 Job,而不是等定时任务。小崔用 REST API + JobLauncher 实现。

@RestController
@RequestMapping("/api/batch")
public class BatchJobController {

    private final JobLauncher jobLauncher;
    private final Job orderProcessingJob;

    public BatchJobController(JobLauncher jobLauncher,
                               @Qualifier("orderProcessingJob") Job job) {
        this.jobLauncher = jobLauncher;
        this.orderProcessingJob = job;
    }

    @PostMapping("/orders/process")
    public ResponseEntity<Map<String, Object>> processOrders(
            @RequestParam String date) {
        try {
            JobParameters params = new JobParametersBuilder()
                    .addString("orderDate", date)
                    .addLong("triggeredAt", System.currentTimeMillis())
                    .toJobParameters();

            JobExecution execution = jobLauncher.run(orderProcessingJob, params);

            Map<String, Object> response = new HashMap<>();
            response.put("jobId", execution.getJobId());
            response.put("jobInstanceId", execution.getJobInstance().getId());
            response.put("status", execution.getStatus().name());
            response.put("startTime", execution.getStartTime());
            return ResponseEntity.ok(response);

        } catch (JobInstanceAlreadyCompleteException e) {
            return ResponseEntity.status(409)
                    .body(Map.of("error", "该日期的订单已处理完成"));
        } catch (Exception e) {
            return ResponseEntity.status(500)
                    .body(Map.of("error", e.getMessage()));
        }
    }

    @GetMapping("/orders/status/{executionId}")
    public ResponseEntity<Map<String, Object>> getStatus(
            @PathVariable Long executionId,
            JobExplorer jobExplorer) {
        JobExecution execution = jobExplorer.getJobExecution(executionId);
        if (execution == null) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(Map.of(
            "executionId", execution.getId(),
            "status", execution.getStatus().name(),
            "startTime", execution.getStartTime(),
            "endTime", execution.getEndTime(),
            "exitCode", execution.getExitStatus().getExitCode()
        ));
    }
}

运行结果:

POST /api/batch/orders/process?date=2026-06-13
→ 200 OK
{
  "jobId": 42,
  "jobInstanceId": 15,
  "status": "STARTED",
  "startTime": "2026-06-13T02:00:00"
}

// 等待 30 秒后查询状态
GET /api/batch/orders/status/42
→ 200 OK
{
  "executionId": 42,
  "status": "COMPLETED",
  "startTime": "2026-06-13T02:00:00",
  "endTime": "2026-06-13T02:00:25",
  "exitCode": "COMPLETED"
}

场景二:定时任务触发——@Scheduled 集成

@Component
public class ScheduledBatchRunner {

    private final JobLauncher jobLauncher;
    private final Job nightlyReportJob;

    public ScheduledBatchRunner(JobLauncher jobLauncher,
                                 @Qualifier("nightlyReportJob") Job job) {
        this.jobLauncher = jobLauncher;
        this.nightlyReportJob = job;
    }

    @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨 2 点
    public void runNightlyReport() throws Exception {
        JobParameters params = new JobParametersBuilder()
                .addString("reportDate", LocalDate.now().toString())
                .addLong("run.id", System.currentTimeMillis())
                .toJobParameters();

        JobExecution execution = jobLauncher.run(nightlyReportJob, params);
        System.out.println("Nightly report completed: " + execution.getStatus());
    }
}

易错场景与避坑

反例一:同步模式下 Web 请求超时

// ❌ 问题:同步 JobLauncher 在 Web 请求中阻塞 → 请求超时
@PostMapping("/jobs/long-running")
public String runLongJob() throws Exception {
    // 这个 Job 要跑 10 分钟,HTTP 请求 30 秒就超时了
    JobExecution execution = jobLauncher.run(longJob, params);
    return "done"; // 永远到不了这里
}

解决方案:使用异步 JobLauncher 或异步 Controller:

// ✅ 方案一:异步 JobLauncher
@Bean
public JobLauncher asyncJobLauncher(JobRepository jobRepository) {
    SimpleJobLauncher launcher = new SimpleJobLauncher();
    launcher.setJobRepository(jobRepository);
    launcher.setTaskExecutor(new SimpleAsyncTaskExecutor());
    return launcher;
}

// ✅ 方案二:Controller 用 @Async
@Async
@PostMapping("/jobs/long-running")
public CompletableFuture<String> runLongJob() throws Exception {
    JobExecution execution = jobLauncher.run(longJob, params);
    return CompletableFuture.completedFuture(
        "Job " + execution.getId() + ": " + execution.getStatus());
}

反例二:异步模式下忘记检查线程安全

// ❌ 异步模式 + 非线程安全的 Reader → 数据错乱
@Bean
public FlatFileItemReader<Order> reader() {
    return new FlatFileItemReaderBuilder<Order>()
            .name("reader")
            .resource(new FileSystemResource("orders.csv"))
            .saveState(false) // ❌ 关闭状态保存 + 异步 → 并发问题
            .build();
}

正确做法:异步模式下确保 Reader/Writer 是线程安全的,或使用 @StepScope 为每个线程创建独立的 Bean 实例。

面试高频考点

Q1:SimpleJobLauncher 的同步和异步模式有什么区别?

同步模式 run() 阻塞直到 Job 完成,适用于命令行和简单场景。异步模式通过设置 TaskExecutor 使 run() 立即返回,Job 在后台线程执行。异步模式需要在 Spring Boot 配置中设置 spring.batch.job.enabled=false 防止启动时自动执行。

Q2:多个 JobLauncher 可以共存吗?

可以。通过在 @Bean 方法上使用 @Qualifier 区分。常见场景:一个同步 JobLauncher 用于命令行,一个异步 JobLauncher 用于 Web 请求。

Q3:JobLauncher 和 JobOperator 有什么区别?

JobLauncher 只负责启动 Job。JobOperator 是一个更高级的接口,聚合了 JobLauncher(启动)+ JobExplorer(查询)+ JobRepository(状态管理),提供 start/stop/restart/abandon 等完整的作业生命周期控制。


上一章:JobParameters 详解下一章:JobRepository 详解

上一页
Job 配置
下一页
JobParameters 详解