弹簧批次@BeforeStep不适用于@StepScope

2022-09-01 09:35:55

我使用的是Spring Batch版本2.2.4.RELEASE,我试图用有状态的ItemReader,ItemProcessor和ItemWriter beans编写一个简单的例子。

public class StatefulItemReader implements ItemReader<String> {

    private List<String> list;

    @BeforeStep
    public void initializeState(StepExecution stepExecution) {
        this.list = new ArrayList<>();
    }

    @AfterStep
    public ExitStatus exploitState(StepExecution stepExecution) {
        System.out.println("******************************");
        System.out.println(" READING RESULTS : " + list.size());

        return stepExecution.getExitStatus();
    }

    @Override
    public String read() throws Exception {
        this.list.add("some stateful reading information");
        if (list.size() < 10) {
            return "value " + list.size();
        }
        return null;
    }
}

在我的集成测试中,我在内部静态java配置类中声明我的bean,如下所示:

@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class SingletonScopedTest {

    @Configuration
    @EnableBatchProcessing
    static class TestConfig {
        @Autowired
        private JobBuilderFactory jobBuilder;
        @Autowired
        private StepBuilderFactory stepBuilder;

        @Bean
        JobLauncherTestUtils jobLauncherTestUtils() {
            return new JobLauncherTestUtils();
        }

        @Bean
        public DataSource dataSource() {
            EmbeddedDatabaseBuilder embeddedDatabaseBuilder = new EmbeddedDatabaseBuilder();
            return embeddedDatabaseBuilder.addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql")
                    .addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql")
                    .setType(EmbeddedDatabaseType.HSQL)
                    .build();
        }

        @Bean
        public Job jobUnderTest() {
            return jobBuilder.get("job-under-test")
                    .start(stepUnderTest())
                    .build();
        }

        @Bean
        public Step stepUnderTest() {
            return stepBuilder.get("step-under-test")
                    .<String, String>chunk(1)
                    .reader(reader())
                    .processor(processor())
                    .writer(writer())
                    .build();
        }

        @Bean
        public ItemReader<String> reader() {
            return new StatefulItemReader();
        }

        @Bean
        public ItemProcessor<String, String> processor() {
            return new StatefulItemProcessor();
        }

        @Bean
        public ItemWriter<String> writer() {
            return new StatefulItemWriter();
        }
    }

    @Autowired
    JobLauncherTestUtils jobLauncherTestUtils;

    @Test
    public void testStepExecution() {
        JobExecution jobExecution = jobLauncherTestUtils.launchStep("step-under-test");

        assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
    }
}

此测试通过。

但是,一旦我将 StatefulItemReader 定义为步范围 Bean(这对于有状态读取器来说更好),“前步”代码就不再执行。

...
    @Bean
    @StepScope
    public ItemReader<String> reader() {
        return new StatefulItemReader();
    }
...

我注意到处理器和我的作家豆也存在同样的问题。

我的代码出了什么问题?是否与此已解决的问题有关:https://jira.springsource.org/browse/BATCH-1230

我的整个 Maven 项目包含多个 JUnit 测试,可以在 GitHub 上找到:https://github.com/galak75/spring-batch-step-scope

提前感谢您的回答。


答案 1

按如下方式配置 Bean 时:

@Bean
@StepScope
public MyInterface myBean() {
    return new MyInterfaceImpl();
}

您正在告诉Spring使用 代理模式 。但是,通过返回 ,而不是 ,代理只能看到 上的方法。这阻止了 Spring Batch 能够找到已使用侦听器注释(如 .配置此内容的正确方法是返回您的配置方法,如下所示:ScopedProxyMode.TARGET_CLASSMyInterfaceMyInterfaceImplMyInterfaceMyInterfaceImpl@BeforeStepMyInterfaceImpl

@Bean
@StepScope
public MyInterfaceImpl myBean() {
    return new MyInterfaceImpl();
}

我们在启动时添加了一条警告日志消息,指出当我们查找带注释的侦听器方法时,如果对象是代理的,并且目标是接口,我们将无法在实现类上找到带有注释的方法。


答案 2

正如pojo-guy所建议的那样,解决方案是实现StepExecutionListener和Overovered beforeStep方法来设置stepExecution

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

推荐