无法将作业参数传递给 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);
}
我尝试了以下解决方案:
- 使用 jobExecutionContext 和 stepExecutionContext 而不是 jobParameters。结果我得到了空对象而不是传递的参数;
从参数名称周围删除简单引号(例如#jobParameters[EMPCODE]),但随后出现此错误:
Invalid property 'EMPCODE' of bean class [org.springframework.batch.core.scope.context.StepContext]: Bean property 'EMPCODE' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
我只是不知道我做错了什么,因为根据文档和我在网上发现的其他帖子,一切似乎都很好。
编辑:我正在使用 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
注释自动定义(很像 jobLauncher
或 jobBuilders
)。因此,在您的配置中声明一个名为 jobParameters
的 bean 将导致与已声明的同名 bean 发生冲突,从而导致编译错误。
为避免这种情况,请在配置之外声明您的参数,或者以不同的方式命名您的 bean,例如jobParams
.
我正在使用 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);
}
我尝试了以下解决方案:
- 使用 jobExecutionContext 和 stepExecutionContext 而不是 jobParameters。结果我得到了空对象而不是传递的参数;
从参数名称周围删除简单引号(例如#jobParameters[EMPCODE]),但随后出现此错误:
Invalid property 'EMPCODE' of bean class [org.springframework.batch.core.scope.context.StepContext]: Bean property 'EMPCODE' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
我只是不知道我做错了什么,因为根据文档和我在网上发现的其他帖子,一切似乎都很好。
编辑:我正在使用 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
注释自动定义(很像 jobLauncher
或 jobBuilders
)。因此,在您的配置中声明一个名为 jobParameters
的 bean 将导致与已声明的同名 bean 发生冲突,从而导致编译错误。
为避免这种情况,请在配置之外声明您的参数,或者以不同的方式命名您的 bean,例如jobParams
.