春季批处理和SystemCommandTasklet:无法传递参数

huangapple go评论96阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2020年3月17日 00:27:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/60709696.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定