Spring Boot 3模块化上下文批处理应用指定任务无法启动
Spring Boot 3模块化批处理指定任务无法启动问题
问题描述
升级到Spring Boot 3后,通过参数--spring.batch.job.names指定批处理任务时,任务完全无法启动。执行命令java -jar mybatchapplication.jar --spring.batch.job.names=jobA后,应用启动完成后直接关闭,尝试--spring.batch.job.name参数也无效。
核心代码
主类
@SpringBootApplication @EnableJpaRepositories("path.to.my.repositories") @EntityScan("path.to.my.entities") public class MyBatchApplication{ public static void main(String[] args) { System.exit(SpringApplication.exit(SpringApplication.run(MyBatchApplication.class, args))); } }
模块化配置类
@Configuration @EnableBatchProcessing(modular = true) public class MyModularBatchJobContextConfiguration { @Bean ApplicationContextFactory getJobAContext() { return new GenericApplicationContextFactory(JobAConfig.class); } @Bean ApplicationContextFactory getJobBContext() { return new GenericApplicationContextFactory(JobBConfig.class); } }
任务配置类(JobAConfig示例)
public class JobAConfig { private static final String MY_JOB_NAME = "jobA"; @Autowired private EntityManagerFactory emf; @Bean public Job jobA(JobRepository jobRepository, Step myStep) { return new JobBuilder(MY_JOB_NAME, jobRepository) .flow(myStep) .end() .build(); } @Bean public Step myStep(JpaTransactionManager transactionManager, JobRepository jobRepository, FlatFileItemReader<MyEntity> reader, ItemProcessor<MyEntity, MyEntity> processor, JpaItemWriter<MyEntity> entityWriter) { return new StepBuilder(MY_STEP_NAME, jobRepository) .<MyEntity, MyEntity>chunk(10, transactionManager) .reader(reader) .processor(processor) .writer(entityWriter) .transactionManager(transactionManager) .build(); } // Reader, Writer, Processor... }
日志信息
2023-05-11T22:05:21.823+02:00 INFO 9716 --- [ main] d.f.r.batch.MyBatchApplication: Starting MyBatchApplicationusing Java 17.0.5 with PID 9716 (C:\gits\mygit\batch\target\mybatchapplication.jar started by me in C:\gits\mygit) 2023-05-11T22:05:21.825+02:00 INFO 9716 --- [ main] d.f.r.batch.MyBatchApplication: No active profile set, falling back to 1 default profile: "default" 2023-05-11T22:05:22.112+02:00 INFO 9716 --- [ main] o.s.b.c.c.annotation.BatchRegistrar : Finished Spring Batch infrastructure beans configuration in 6 ms. 2023-05-11T22:05:22.118+02:00 INFO 9716 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 2023-05-11T22:05:22.149+02:00 INFO 9716 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 25 ms. Found 2 JPA repository interfaces. 2023-05-11T22:05:22.571+02:00 INFO 9716 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 2023-05-11T22:05:22.616+02:00 INFO 9716 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.1.7.Final 2023-05-11T22:05:22.895+02:00 INFO 9716 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2023-05-11T22:05:22.995+02:00 INFO 9716 --- [ main] com.zaxxer.hikari.pool.PoolBase : HikariPool-1 - Driver does not support get/set network timeout for connections. (Receiver class org.h2.jdbc.JdbcConnection does not define or inherit an implementation of the resolved method 'abstract int getNetworkTimeout()' of interface java.sql.Connection.) 2023-05-11T22:05:22.998+02:00 INFO 9716 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:file:~/mydb user=SA 2023-05-11T22:05:22.999+02:00 INFO 9716 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2023-05-11T22:05:23.016+02:00 INFO 9716 --- [ main] SQL dialect : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect 2023-05-11T22:05:23.542+02:00 INFO 9716 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform] 2023-05-11T22:05:23.552+02:00 INFO 9716 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2023-05-11T22:05:23.588+02:00 INFO 9716 --- [ main] o.s.b.c.r.s.JobRepositoryFactoryBean : No database type set, using meta data indicating: H2 2023-05-11T22:05:23.613+02:00 INFO 9716 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor. 2023-05-11T22:05:24.294+02:00 WARN 9716 --- [ main] o.s.b.c.l.AbstractListenerFactoryBean : org.springframework.batch.item.ItemProcessor is an interface. The implementing class will not be queried for annotation based listener configurations. If using @StepScope on a @Bean method, be sure to return the implementing class so listener annotations can be used. 2023-05-11T22:05:24.322+02:00 INFO 9716 --- [ main] .c.a.BatchObservabilityBeanPostProcessor : No Micrometer observation registry found, defaulting to ObservationRegistry.NOOP 2023-05-11T22:05:24.358+02:00 INFO 9716 --- [ main] .c.a.BatchObservabilityBeanPostProcessor : No Micrometer observation registry found, defaulting to ObservationRegistry.NOOP 2023-05-11T22:05:24.370+02:00 WARN 9716 --- [ main] o.s.b.c.l.AbstractListenerFactoryBean : org.springframework.batch.item.ItemProcessor is an interface. The implementing class will not be queried for annotation based listener configurations. If using @StepScope on a @Bean method, be sure to return the implementing class so listener annotations can be used. 2023-05-11T22:05:24.371+02:00 INFO 9716 --- [ main] .c.a.BatchObservabilityBeanPostProcessor : No Micrometer observation registry found, defaulting to ObservationRegistry.NOOP 2023-05-11T22:05:24.371+02:00 INFO 9716 --- [ main] .c.a.BatchObservabilityBeanPostProcessor : No Micrometer observation registry found, defaulting to ObservationRegistry.NOOP 2023-05-11T22:05:24.395+02:00 INFO 9716 --- [ main] d.f.r.batch.MyBatchApplication: Started MyBatchApplicationin 2.848 seconds (process running for 3.242) 2023-05-11T22:05:24.406+02:00 INFO 9716 --- [ main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 2023-05-11T22:05:24.410+02:00 INFO 9716 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 2023-05-11T22:05:24.417+02:00 INFO 9716 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
问题原因
在Spring Boot 3 + Spring Batch的模块化配置中,--spring.batch.job.names参数默认只会扫描主ApplicationContext中的Job bean,而你的JobA、JobB都在通过GenericApplicationContextFactory创建的子ApplicationContext里,主上下文无法直接找到这些Job,导致批处理自动启动逻辑不触发,应用启动后直接关闭。
解决方法
方法一:使用子上下文前缀指定任务名
模块化批处理中,子上下文的Job bean会自动带上子上下文ID作为前缀,默认前缀是配置类的全限定名(比如path.to.JobAConfig)。你需要用前缀.任务名的形式指定:
java -jar mybatchapplication.jar --spring.batch.job.names=path.to.JobAConfig.jobA
或者给GenericApplicationContextFactory自定义ID,简化前缀:
@Bean ApplicationContextFactory getJobAContext() { return new GenericApplicationContextFactory(JobAConfig.class).setId("jobAContext"); }
之后用自定义ID作为前缀:
java -jar mybatchapplication.jar --spring.batch.job.names=jobAContext.jobA
方法二:自定义JobLocator扫描所有子上下文
自定义JobLocator来统一管理所有子上下文的Job,让批处理自动配置能找到这些任务:
@Configuration @EnableBatchProcessing(modular = true) public class MyModularBatchJobContextConfiguration { @Bean ApplicationContextFactory getJobAContext() { return new GenericApplicationContextFactory(JobAConfig.class); } @Bean ApplicationContextFactory getJobBContext() { return new GenericApplicationContextFactory(JobBConfig.class); } @Bean public JobLocator jobLocator(List<ApplicationContextFactory> contextFactories) { ModularJobLocator modularJobLocator = new ModularJobLocator(); modularJobLocator.setApplicationContextFactories(contextFactories); return modularJobLocator; } }
方法三:手动实现任务触发
通过CommandLineRunner手动解析命令行参数,遍历子上下文查找并启动指定任务:
@Component public class BatchJobRunner implements CommandLineRunner { private final JobLauncher jobLauncher; private final List<ApplicationContextFactory> contextFactories; public BatchJobRunner(JobLauncher jobLauncher, List<ApplicationContextFactory> contextFactories) { this.jobLauncher = jobLauncher; this.contextFactories = contextFactories; } @Override public void run(String... args) throws Exception { String jobNames = Arrays.stream(args) .filter(arg -> arg.startsWith("--spring.batch.job.names=")) .map(arg -> arg.split("=", 2)[1]) .findFirst() .orElse(null); if (jobNames == null) { return; } for (String jobName : jobNames.split(",")) { for (ApplicationContextFactory factory : contextFactories) { ApplicationContext context = factory.createApplicationContext(); if (context.containsBean(jobName)) { Job job = context.getBean(jobName, Job.class); jobLauncher.run(job, new JobParameters()); break; } } } } }
内容的提问来源于stack exchange,提问作者Manu
相关产品推荐
相关产品推荐

