spring-batch
spring-batch copied to clipboard
improve JobParameters to Non ThreadLocal
JobParameters are managed by org.springframework.batch.core.scope.context.SynchronizationManagerSupport#executionHolder using ThreadLocal, which prevents SpEL bindings (@Value("#{jobParameters['param']}")) from working correctly in other threads.
However, JobParameters are typically read-only and do not change during batch execution. In a multi-threaded step (TaskExecutor), this ThreadLocal management causes access to JobParameters within JobScope or StepScope beans to fail.
I’m not sure why JobParameters need to be managed within a ThreadLocal JobContext. Could JobParameters be modified to be non-ThreadLocal instead?
I’m using a class that encapsulates JobParameters. However, access to this class's fields fails when accessed within a JobScope (or StepScope) bean in a multi-threaded step.
encapsulates JobParameters
@JobScope
@Component
public class MyJobParameters {
@Value("#{jobParameters['firstParam']}")
private String firstParam;
@Value("#{jobParameters['secondParam']}")
private String secondParam;
public String getFirstParam() {
return firstParam;
}
public String getSecondParam() {
return secondParam;
}
}
failed (multi thread step)
public Step myMultiThreadStep() {
return new StepBuilder("chunkWorkingStep", jobRepository)
.<Sample, Sample>chunk(500, new ResourcelessTransactionManager())
.reader(myReader())
.processor(myProcessor())
.writer(myWriter())
.taskExecutor(taskExecutor())
.build();
}
public ItemProcessor<Sample, Sample> myProcessor() {
String firstParam = myJobParameters.getFirstParam(); // failed
return item -> {
String secondParam = myJobParameters.getSecondParam(); // failed
return item;
};
}
ok (non multi thread step)
public Step myStep() {
return new StepBuilder("chunkWorkingStep", jobRepository)
.<Sample, Sample>chunk(500, new ResourcelessTransactionManager())
.reader(myReader())
.processor(myProcessor())
.writer(myWriter())
.build();
}
public ItemProcessor<Sample, Sample> myProcessor() {
String firstParam = myJobParameters.getFirstParam(); // ok
return item -> {
String secondParam = myJobParameters.getSecondParam(); // ok
return item;
};
}
If there’s anything I might be missing or not understanding, please let me know.
I agree, job parameters are not expected to change during a job execution.
I’m not sure why JobParameters need to be managed within a ThreadLocal JobContext. Could JobParameters be modified to be non-ThreadLocal instead?
I am not sure this is straightforward to do with the current design of having the job parameters as part of the context. That said, is there any reason why MyJobParameters is job-scoped? What's the added value of this encapsulation? Unless there is a specific reason, I would inject job parameters where they need to be used directly, something like this based on your example:
public ItemProcessor<Sample, Sample> myProcessor(@Value("#{jobParameters['param']}") String param) {
return item -> {
// use param as needed here
return item;
};
}