无法将作业参数传递给 Spring 批处理中的步骤作用域 bean

Impossible to pass job parameter to step scoped bean in Spring Batch

我正在使用 Spring Batch 编写批处理,并试图将作业参数传递给项目 reader bean 定义,但是当我执行批处理时,我不断得到以下信息错误:

org.springframework.expression.spel.SpelEvaluationException: EL1027E:(pos 13): Indexing into type 'org.springframework.batch.core.JobParameters' is not supported

配置代码如下class:

/**
 * Job producing an XML file aimed at synchronizing Appipay data with ForHRM data
 *
 * @author francois.dupire
 */
@Configuration
@Import(BatchConfiguration.class)
@ComponentScan(basePackageClasses = BusinessObjectServiceImpl.class)
public class ForHRMToAppipayDescriptiveDataSyncJobConfiguration {

    /*
     * Constants
     */
    public static final String JOB_NAME = "forHRMToAppipaySyncJob";
    private static final String FORHRM_TO_APPIPAY_SYNC_STEP = "forHRMToAppipaySyncStep";

    private static final int CHUNK_SIZE = 10;

    /*
     * Fields
     */
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    /*
     * Constructors
     */
    @Autowired
    public ForHRMToAppipayDescriptiveDataSyncJobConfiguration(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
        this.jobBuilderFactory = jobBuilderFactory;
        this.stepBuilderFactory = stepBuilderFactory;
    }

    /*
     * Job
     */
    @Bean
    public Job forHRMToAppipaySyncJob(Step forHRMToAppipaySyncStep) {
        return jobBuilderFactory
                .get(JOB_NAME)
                .start(forHRMToAppipaySyncStep)
                .build();
        // TODO Add parameters validator
    }

    /*
     * Steps
     */
    // Sync step
    @Bean
    public Step forHRMToAppipaySyncStep(AppipaySyncTriggersReader reader, AppipaySyncTriggersToAppipayDescriptiveDataProcessor processor, AppipayDescriptiveDataWriter writer) {
        return stepBuilderFactory
                .get(FORHRM_TO_APPIPAY_SYNC_STEP)
                .<List<SynchronisationAppipay>, Signaletiques>chunk(CHUNK_SIZE)
                .reader(reader)
                .processor(processor)
                .writer(writer)
                .build();
    }

    @Bean
    @StepScope
    public AppipaySyncTriggersReader appipaySyncTriggersReader(@Value("#{jobParameters['EMPCODE']}") String employerCode, SynchronisationAppipayRepository appipaySyncTriggerRepository) {
        return new AppipaySyncTriggersReader(employerCode, CHUNK_SIZE, appipaySyncTriggerRepository);
    }

我尝试了以下解决方案:

我只是不知道我做错了什么,因为根据文档和我在网上发现的其他帖子,一切似乎都很好。

编辑:我正在使用 Spring 4.3.3 和 Spring Batch 3.0.7

抱歉,这可能不是一个好的答案,但评论太长了。

我不记得这一切是如何工作的,但我有一个 JobLauncherDetails class,它基本上就是提供的示例。

我认为关键是必须有人实现 JobLauncherDetails class -- 我的扩展了 QuartzJobBean。但是无论如何,某人必须从 spring-batch BATCH_JOB_EXECUTION_PARAMS table 中检索此数据。我认为这可能是你所缺少的。

无论如何,这是代码,以防对您有所帮助

public class JobLauncherDetails extends QuartzJobBean {

/**
 * Special key in job data map for the name of a job to run.
 */
static final String JOB_NAME = "jobName";

static final Logger logger = LogManager.getLogger();
{
    String threadId = String.valueOf(Thread.currentThread().getId());
    ThreadContext.put("TId", threadId);
}

private JobLocator jobLocator;

private JobLauncher jobLauncher;

/**
 * Public setter for the {@link JobLocator}.
 * @param jobLocator the {@link JobLocator} to set
 */
public void setJobLocator(JobLocator jobLocator) {
    this.jobLocator = jobLocator;
}

/**
 * Public setter for the {@link JobLauncher}.
 * @param jobLauncher the {@link JobLauncher} to set
 */
public void setJobLauncher(JobLauncher jobLauncher) {
    this.jobLauncher = jobLauncher;
}



/*
 * Copy parameters that are of the correct type over to
 * {@link JobParameters}, ignoring jobName.
 *
 * @return a {@link JobParameters} instance
 */
private JobParameters getJobParametersFromJobMap(Map<String, Object> jobDataMap) {

    JobParametersBuilder builder = new JobParametersBuilder();

    for (Entry<String, Object> entry : jobDataMap.entrySet()) {
        String key = entry.getKey();
        Object value = entry.getValue();
        if (value instanceof String && !key.equals(JOB_NAME)) {
            builder.addString(key, (String) value);
        }
        else if (value instanceof Float || value instanceof Double) {
            builder.addDouble(key, ((Number) value).doubleValue());
        }
        else if (value instanceof Integer || value instanceof Long) {
            builder.addLong(key, ((Number)value).longValue());
        }
        else if (value instanceof Date) {
            builder.addDate(key, (Date) value);
        }
        else {
            logger.debug("JobDataMap contains values which are not job parameters (ignoring).");
        }
    }

    return builder.toJobParameters();

}


@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws org.quartz.JobExecutionException {
    Map<String, Object> jobDataMap = jobExecutionContext.getMergedJobDataMap();
    String jobName = (String) jobDataMap.get(JOB_NAME);
    logger.info("Quartz trigger firing with Spring Batch jobName="+jobName);
    JobParameters jobParameters = getJobParametersFromJobMap(jobDataMap);
    try {
        jobLauncher.run(jobLocator.getJob(jobName), jobParameters);
    }
    catch (JobExecutionException e) {
        logger.error("Could not execute job.", e);
    }
}

}

我得到了问题的答案。

在我导入配置的 BatchConfiguration 中,有一个 jobParameters bean 优先于传递给作业的参数。所以在这个 bean 中找不到我的论据。我删除了它,它解决了我的问题。

详细说明 OP 的回答:显然,当您想在 Spring 批处理中访问作业参数时,您总是必须将它们称为 jobParameters

包含传入参数的实例由 @EnableBatchProcessing 注释自动定义(很像 jobLauncherjobBuilders)。因此,在您的配置中声明一个名为 jobParameters 的 bean 将导致与已声明的同名 bean 发生冲突,从而导致编译错误。

为避免这种情况,请在配置之外声明您的参数,或者以不同的方式命名您的 bean,例如jobParams.