You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Batch定时任务重复调用同一ExcelWriter问题及解决方法

Spring Batch定时任务生成独立Excel文件失败问题解决

问题现象

首次使用Spring Batch开发定时任务,从数据库取数、处理后通过ExcelWriter写入Excel的基础功能正常,但想要实现每次定时任务生成独立Excel文件时失败。日志显示尽管每次任务都手动创建新的ExcelWriter实例,但实际执行时始终复用同一个实例。

核心代码片段

BatchConfig配置类

@Configuration
@EnableBatchProcessing
public class BatchConfig {
    private final DataSource dataSource;
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    public BatchConfig(DataSource dataSource, JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
        this.dataSource = dataSource;
        this.jobBuilderFactory = jobBuilderFactory;
        this.stepBuilderFactory = stepBuilderFactory;
    }

    @Bean
    public Job transactionExportJob(Step exportStep, BatchJobCompletionListener listener) {
        return jobBuilderFactory.get("transactionExportJob")
                .incrementer(new RunIdIncrementer())
                .start(exportStep)
                .listener(listener)
                .build();
    }

    @Bean
    public Step exportStep(ItemReader<Transaction> reader, ExcelWriter excelWriter, PlatformTransactionManager transactionManager) {
        return stepBuilderFactory.get("exportStep")
                .<Transaction, Transaction>chunk(5000)
                .reader(reader)
                .processor(new TransactionProcessor()) // handle data
                .writer(excelWriter)
                .faultTolerant() // enabling fault tolerance
                .retryLimit(3) 
                .retry(Exception.class)
                .skip(Exception.class)
                .skipLimit(100)
                .listener(new CustomSkipListener()) // listing skip
                .transactionManager(transactionManager)
                .build();
    }

    @Bean
    public JdbcPagingItemReader<Transaction> reader(PagingQueryProvider transactionQueryProvider) {
        JdbcPagingItemReader<Transaction> reader = new JdbcPagingItemReader<>();
        reader.setDataSource(dataSource);
        reader.setFetchSize(5000);
        reader.setRowMapper(new TransactionRowMapper());
        reader.setQueryProvider(transactionQueryProvider);

        return reader;
    }
}

ExcelWriter类

@Component
public class ExcelWriter implements ItemWriter<Transaction> {
    private final String filePath;
    private final Workbook workbook;
    private final Sheet sheet;
    private int rowCount = 1;

    public ExcelWriter() {
        this.filePath = "transactions_" + UUID.randomUUID().toString() + ".xlsx";
        this.workbook = new XSSFWorkbook();
        this.sheet = workbook.createSheet("Transactions");
        createHeaderRow();
    }

    @Override
    public void write(List<? extends Transaction> items) throws Exception {
        System.out.println("filePath: " + filePath);
        for (Transaction transaction : items) {
            Row row = sheet.createRow(rowCount++);
            row.createCell(0).setCellValue(transaction.getUniqueTxId());
            row.createCell(1).setCellValue(transaction.getCreatedTime().toString());
        }
        try (FileOutputStream out = new FileOutputStream(filePath)) {
            workbook.write(out);
        } catch (IOException e) {
            System.err.println("Error closing Excel file: " + e.getMessage());
            throw new IOException("Failed to close workbook", e);
        }
    }

    private void createHeaderRow() {
        Row header = sheet.createRow(0);
        header.createCell(0).setCellValue("Unique Tx Id");
        header.createCell(1).setCellValue("Created Time");
    }

    public void closeWorkbook() throws IOException {
        workbook.close();
    }
}

BatchScheduler调度类

@Component
@EnableScheduling
public class BatchScheduler {
    private final JobLauncher jobLauncher;
    private final Job transactionExportJob;
    private final JobParametersHolder jobParametersHolder;


    public BatchScheduler(JobLauncher jobLauncher, Job transactionExportJob, JobParametersHolder jobParametersHolder) {
        this.jobLauncher = jobLauncher;
        this.transactionExportJob = transactionExportJob;
        this.jobParametersHolder = jobParametersHolder;
    }

    @Scheduled(fixedRate = 60000, initialDelay = 60000, zone = "Asia/Shanghai")
    public void runBatchJob() {
        try {
            ExcelWriter excelWriter = new ExcelWriter();
            LocalDateTime startTime = LocalDateTime.now().minusDays(1).withHour(22);
            LocalDateTime endTime = LocalDateTime.now().withHour(22);

            JobParameters jobParameters = new JobParametersBuilder()
                    .addDate("startTime", Date.from(startTime.atZone(ZoneId.of("Asia/Shanghai")).toInstant()))
                    .addDate("endTime", Date.from(endTime.atZone(ZoneId.of("Asia/Shanghai")).toInstant()))
                    .toJobParameters();

            System.out.println("jobParameters: " + jobParameters);
            jobParametersHolder.setJobParameters(jobParameters);

            System.out.println("Batch Job started");
            JobExecution execution = jobLauncher.run(transactionExportJob, jobParameters);
            System.out.println("Batch job completed with status: {}" + execution.getStatus());
            excelWriter.closeWorkbook();
        } catch (Exception e) {
            System.out.println("Batch job execution failed: {}" + e.getMessage() + e);
        }
    }
}

解决方案

移除ExcelWriter类上的@Component注解,改为使用@JobScope注解标记该类,这样每次Job运行时都会重新创建对应的ExcelWriter实例,实现每次生成独立Excel文件的需求。

内容的提问来源于stack exchange,提问作者LaiFQZzr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 05:03:31