英文:
Spring batch and SystemCommandTasklet: cannot pass parameter
问题
我正在尝试从一个REST控制器触发一个Spring Batch作业,并且我需要将参数传递给Step。我使用了SystemCommandTasklet,并且如此处所建议(https://stackoverflow.com/questions/42142126/spring-batch-systemcommandtasklet-throwing-null-pointer-exception),我将Tasklet添加为StepListener,但是我一直收到一个NPE。
我已经在这里创建了一个示例项目(https://github.com/vforchi/spring-batch-problem):如果您运行测试,您将在输出中看到以下内容:
java.lang.NullPointerException: null
at org.springframework.batch.core.step.tasklet.SystemCommandTasklet.execute(SystemCommandTasklet.java:133) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
...
Tasklet中的beforeStep
方法从未被调用,这导致了NPE。
这是我的批处理配置:
@Configuration
@EnableBatchProcessing
public class BatchConfiguration extends DefaultBatchConfigurer {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
public BatchConfiguration(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
this.jobBuilderFactory = jobBuilderFactory;
this.stepBuilderFactory = stepBuilderFactory;
}
@Override
public JobLauncher getJobLauncher() {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(getJobRepository());
jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
return jobLauncher;
}
@Bean
public Job myJob(Step myStep) {
return jobBuilderFactory.get("myJob")
.incrementer(new RunIdIncrementer())
.start(myStep)
.build();
}
@Bean
public Step myStep(Tasklet myServiceTasklet) {
return this.stepBuilderFactory.get("myStep")
.listener(myServiceTasklet)
.tasklet(myServiceTasklet)
.build();
}
@Bean
@StepScope
public Tasklet myServiceTasklet(
@Value("#{jobParameters['my_param']}") String param
) {
SystemCommandTasklet tasklet = new SystemCommandTasklet();
System.out.println(param);
tasklet.setCommand("sleep 10");
tasklet.setTimeout(20000);
return tasklet;
}
}
我尝试了许多不同的方法,但我仍然无法弄清楚为什么Tasklet中的beforeStep
方法没有被调用,导致execution
字段保持为null
。
英文:
I am trying to trigger a spring batch job from a rest controller, and I need to pass parameters to the Step. I used a SystemCommandTasklet and, as suggested here I added the Tasklet as a StepListener, but I keep getting a NPE.
I have created a sample project here: if you run the test you will see this on the output:
java.lang.NullPointerException: null
at org.springframework.batch.core.step.tasklet.SystemCommandTasklet.execute(SystemCommandTasklet.java:133) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:136) ~[spring-aop-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at com.sun.proxy.$Proxy87.execute(Unknown Source) ~[na:na]
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:410) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:319) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:147) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
The method beforeStep
in the Tasklet is never invoked, and that leads to the NPE.
This is my batch configuration:
@Configuration
@EnableBatchProcessing
public class BatchConfiguration extends DefaultBatchConfigurer {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
public BatchConfiguration(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
this.jobBuilderFactory = jobBuilderFactory;
this.stepBuilderFactory = stepBuilderFactory;
}
@Override
public JobLauncher getJobLauncher() {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(getJobRepository());
jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
return jobLauncher;
}
@Bean
public Job myJob(Step myStep) {
return jobBuilderFactory.get("myJob")
.incrementer(new RunIdIncrementer())
.start(myStep)
.build();
}
@Bean
public Step myStep(Tasklet myServiceTasklet) {
return this.stepBuilderFactory.get("myStep")
.listener(myServiceTasklet)
.tasklet(myServiceTasklet)
.build();
}
@Bean
@StepScope
public Tasklet myServiceTasklet(
@Value("#{jobParameters['my_param']}") String param
) {
SystemCommandTasklet tasklet = new SystemCommandTasklet();
System.out.println(param);
tasklet.setCommand("sleep 10");
tasklet.setTimeout(20000);
return tasklet;
}
}
I tried dozens of different ways to do this, but I still can't figure out why the beforeStep
method in the Tasklet is not invoked and the field execution
remains null
答案1
得分: 0
任务bean定义方法的返回类型应为 SystemCommandTasklet
:
@Bean
@StepScope
public SystemCommandTasklet myServiceTasklet(
@Value("#{jobParameters['my_param']}") String param
) {
SystemCommandTasklet tasklet = new SystemCommandTasklet();
System.out.println(param);
tasklet.setCommand("sleep 10");
tasklet.setTimeout(20000);
return tasklet;
}
目前,它返回 Tasklet
,因此Spring Batch不会将其作为 StepExecutionListener
进行代理(这就是为什么它未注册为侦听器并且不会调用 beforeStep
方法)。
您需要尽可能明确bean定义方法的返回类型,以便Spring Batch可以正确代理您的bean并将其注册到正确的类型。更多详细信息请参见:https://stackoverflow.com/a/21941127/5019386。
注意:我下载了您的代码并注意到您在生产代码中没有像在测试代码中那样在启动时禁用作业。您需要将相同的 application.properties
添加到 src/main/resources
中。
英文:
The return type of your tasklet bean definition method should be SystemCommandTasklet
:
@Bean
@StepScope
public SystemCommandTasklet myServiceTasklet(
@Value("#{jobParameters['my_param']}") String param
) {
SystemCommandTasklet tasklet = new SystemCommandTasklet();
System.out.println(param);
tasklet.setCommand("sleep 10");
tasklet.setTimeout(20000);
return tasklet;
}
Currently, it returns Tasklet
and hence it is not proxied by Spring Batch as a StepExecutionListener
(that's why it is not registered as a listener and the beforeStep
method is not invoked).
You need to be as specific as possible for the return type of bean definition methods so that Spring Batch can correctly proxy your beans and register them with the correct type. More details here: https://stackoverflow.com/a/21941127/5019386.
NB: I downloaded your code and noticed that you do not disable jobs at startup for production code as you do in test code. You need to add the same application.properties
in src/main/resources
as well.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论