【问题标题】:Access execution context from StaxWriterCallback从 StaxWriterCallback 访问执行上下文
【发布时间】:2021-03-02 11:30:39
【问题描述】:

我正在实施一项工作,该工作将从数据库加载数据并将结果写入 xml 文件。 xml 文件将具有基于存储在执行上下文中的某些属性的标头。因此,我想访问执行上下文。我已经在这个工作的其他 beans 中完成了这项工作,但我不知道如何从我的 StaxWriterCallback 中访问执行上下文。

在工作中,我的作家目前被实现为:

@Bean(name = "writer")
@StepScope
public StaxEventItemWriter<Data> writer(@Value("#{jobParameters['path']}") String path) {
        
    StaxEventItemWriter<Data> itemWriter = new StaxEventItemWriter<Data>();
    itemWriter.setVersion("1.0");
    itemWriter.setEncoding("UTF-8");
    itemWriter.setStandalone(false);
        
    itemWriter.setHeaderCallback(headerCallback());
    itemWriter.setFooterCallback(new CustomXMLFooterCallback());
        
    itemWriter.setMarshaller(new XStreamMarshaller());
                
    FileSystemResource file = new FileSystemResource(path + "test.xml");
    itemWriter.setResource(file);
        
    return itemWriter;
}

头部回调实现为:

@Component
public class CustomXMLHeaderCallback implements StaxWriterCallback {
    
    private StepExecution stepExecution;

    @BeforeStep
    public void beforeStep(StepExecution stepExecution) {
        this.stepExecution = stepExecution;
    }

    @Override
    public void write(XMLEventWriter writer) throws IOException {
        
        String currentFile = stepExecution.getExecutionContext().getString("currentFile");

        try {

            XMLEventFactory eventFactory = XMLEventFactory.newInstance();
            XMLEvent end = eventFactory.createDTD("\n");
            XMLEvent tab = eventFactory.createDTD("\t");

            writer.add(eventFactory.createStartElement("", "", "file"));
            writer.add(eventFactory.createCharacters(currentFile.substring(19, 31)));
            writer.add(eventFactory.createEndElement("", "", "file"));
            writer.add(end);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

标头回调被添加到批处理作业中:

@Bean
@StepScope
public CustomXMLHeaderCallback headerCallback() {
    return new CustomXMLHeaderCallback();
}

不幸的是,我收到以下错误:

java.lang.NullPointerException: Cannot invoke "org.springframework.batch.core.StepExecution.getExecutionContext()" because "this.stepExecution" is null
    at com.test.xml.callback.CustomXMLHeaderCallback.write(CustomXMLHeaderCallback.java:39) ~[classes/:na]
    at com.test.xml.callback.CustomXMLHeaderCallback$$FastClassBySpringCGLIB$$e0795ecb.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692) ~[spring-aop-5.3.3.jar:5.3.3]
    at com.test.xml.callback.CustomXMLHeaderCallback$$EnhancerBySpringCGLIB$$e9083311.write(<generated>) ~[classes/:na]
    at org.springframework.batch.item.xml.StaxEventItemWriter.open(StaxEventItemWriter.java:447) ~[spring-batch-infrastructure-4.3.1.jar:4.3.1]
    at org.springframework.batch.item.xml.StaxEventItemWriter$$FastClassBySpringCGLIB$$d105dd1.invoke(<generated>) ~[spring-batch-infrastructure-4.3.1.jar:4.3.1]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.batch.item.xml.StaxEventItemWriter$$EnhancerBySpringCGLIB$$599f2f97.open(<generated>) ~[spring-batch-infrastructure-4.3.1.jar:4.3.1]
    at org.springframework.batch.item.support.CompositeItemStream.open(CompositeItemStream.java:104) ~[spring-batch-infrastructure-4.3.1.jar:4.3.1]
    at org.springframework.batch.core.step.tasklet.TaskletStep.open(TaskletStep.java:311) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:205) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:411) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:147) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.3.jar:5.3.3]
    at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.1.jar:4.3.1]
    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:564) ~[na:na]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.3.jar:5.3.3]
    at com.sun.proxy.$Proxy62.run(Unknown Source) ~[na:na]
    at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.4.2.jar:2.4.2]
    at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.4.2.jar:2.4.2]
    at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.4.2.jar:2.4.2]
    at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.4.2.jar:2.4.2]
    at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.4.2.jar:2.4.2]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:795) ~[spring-boot-2.4.2.jar:2.4.2]
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:785) ~[spring-boot-2.4.2.jar:2.4.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:333) ~[spring-boot-2.4.2.jar:2.4.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1311) ~[spring-boot-2.4.2.jar:2.4.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) ~[spring-boot-2.4.2.jar:2.4.2]
    at com.test.main(MainOutboundDatins.java:18) ~[classes/:na]

【问题讨论】:

    标签: java spring spring-batch


    【解决方案1】:

    如果您希望在CustomXMLHeaderCallback 中的步骤之前调用beforeStep 方法,则需要使用步骤构建器的listener 方法之一将该组件注册为步骤中的侦听器。此处的文档对此进行了解释:Intercepting Step Execution

    否则,您可以使用@Value 注入步骤执行:

    @Value("#{StepExecution}")
    private StepExecution stepExecution;
    

    第三种选择是从步骤执行上下文中注入所需的属性:

    // define your header as follows:
    public class CustomXMLHeaderCallback implements StaxWriterCallback {
    
       private String currentFile;
    
       public CustomXMLHeaderCallback(String currentFile) {
          this.currentFile = currentFile;
       }
    
       @Override
       public void write(XMLEventWriter writer) throws IOException {
          // use this.currentFile as needed here
       }
       
    
    }
    
    // decalre your bean as follows:
    @Bean
    @StepScope
    public CustomXMLHeaderCallback headerCallback(@Value("#{stepExecutionContext['currentFile']}") String currentFile) {
        return new CustomXMLHeaderCallback(currentFile);
    }
    

    我推荐第三个选项,因为它只注入所需的属性,而不是整个步骤执行对象。

    【讨论】:

    • 感谢您的回答。我刚刚实施了您提供的第三个选项。现在我收到以下错误,我无法解释Parameter 0 of constructor in com.test.xml.callback.CustomXMLHeaderCallback required a bean of type 'java.lang.String' that could not be found.
    • 看起来您没有删除 CustomXMLHeaderCallback 上的 @Component,因此 Spring 正在寻找一个 String 类型的 bean 来注入它,但找不到它。在我的回答中,请注意我是如何在 CustomXMLHeaderCallback 上删除 @Component 的,因为它将在 headerCallback 方法中手动创建。
    猜你喜欢
    • 2020-11-11
    • 1970-01-01
    • 1970-01-01
    • 2014-05-31
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2017-05-07
    • 2010-09-08
    相关资源
    最近更新 更多