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
相关产品推荐
相关产品推荐

