Spring Batch集成Quartz时jobParameters无法找到错误如何解决?
问题根因
报错的核心原因是你混淆了组件的生命周期和作用域:
jobParameters是Spring Batch特有的上下文对象,只有在Batch Job运行期间才存在,应用启动初始化阶段根本没有该对象@StepScope仅对Spring Batch的Step相关组件生效,你给Quartz的调度配置、JobDetail、Trigger加该注解完全无效,这些组件是应用启动阶段就要完成初始化的,根本不在Batch的Step上下文中运行
具体修改方案
1. 修正Batch配置类的参数注入
删除配置类类字段上的@Value("#{jobParameters[CLASSAPP]}")注入,将参数移到@StepScope注解的Bean方法入参中,只有Step执行时才会注入参数,不会启动就报错:
// 原类上的@Value注入直接删除 @Scope("step") @Bean public JdbcCursorItemReader<FichierEclate> readerDB(@Value("#{jobParameters[CLASSAPP]}") String ClassApp){ JdbcCursorItemReader<FichierEclate> reader = new JdbcCursorItemReader<>(); reader.setDataSource(ds); reader.setSql(query(ClassApp)); reader.setRowMapper(new FichierEclateRowMapper()); return reader; }
2. 修正ItemProcessor的参数注入
@Scope("step")不能加在方法上,要加在处理器类上,或者你将处理器注册为Bean的时候加@StepScope:
@StepScope // 注解加在类上 @Component public class YourItemProcessor implements ItemProcessor<FichierEclate, TdfFile> { @Value("#{jobParameters[CLASSAPP]}") private String ClassApp; @Override public TdfFile process(FichierEclate item) throws Exception { // 业务逻辑直接使用ClassApp即可 } }
3. 修正Quartz配置类的逻辑错误
Quartz配置类是应用启动阶段初始化的组件,完全不能引用jobParameters,修改如下:
@Configuration public class QuartzConfiguration { // 从配置文件读取Quartz触发器本身的调度周期,或者直接写固定值 @Value("${app.quartz.trigger.cron:0 0/3 * * * ?}") private String TRIGGER_CRON; @Autowired private JobLauncher jobLauncher; @Autowired private JobLocator jobLocator; @Bean public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) { JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor(); jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry); return jobRegistryBeanPostProcessor; } // 删掉无用的StepScope注解 @Bean public JobDetailFactoryBean jobDetailFactoryBean() { JobDetailFactoryBean factory = new JobDetailFactoryBean(); factory.setJobClass(QuartzJobLauncher.class); Map<String, Object> map = new HashMap<>(); map.put("jobName", "JobFinal"); map.put("jobLauncher", jobLauncher); map.put("jobLocator", jobLocator); // 删掉CRON_EXPRESSION的注入,该参数你已经会在QuartzJobLauncher中从数据库读取后传给Batch Job factory.setJobDataAsMap(map); factory.setGroup("etl_group"); factory.setName("etl_job"); factory.setDurability(true); return factory; } @Bean public CronTriggerFactoryBean cronTriggerFactoryBean() { CronTriggerFactoryBean stFactory = new CronTriggerFactoryBean(); stFactory.setJobDetail(jobDetailFactoryBean().getObject()); stFactory.setStartDelay(3000); stFactory.setName("cron_trigger"); stFactory.setGroup("cron_group"); // 使用配置的固定调度周期,不要读取jobParameters stFactory.setCronExpression(TRIGGER_CRON); return stFactory; } @Bean public SchedulerFactoryBean schedulerFactoryBean() { SchedulerFactoryBean scheduler = new SchedulerFactoryBean(); scheduler.setTriggers(cronTriggerFactoryBean().getObject()); scheduler.setJobDetails(jobDetailFactoryBean().getObject()); return scheduler; } }
额外说明
如果你需要根据数据库里每行的CRON表达式,给每个任务创建单独的Quartz触发器,不能用静态@Bean的方式注册触发器,需要在应用启动后动态读取数据库数据,逐个创建触发器注册到Scheduler中,和你当前的单触发器批量跑所有任务的逻辑不同。
内容的提问来源于stack exchange,提问作者Manel BEN AMARA
相关产品推荐
相关产品推荐

