Spring-batch @BeforeStep не работает с @StepScope
Я использую Spring Batch версии 2.2.4.RELEASE Я попытался написать простой пример с бинами StateReader, ItemProcessor и ItemWriter с состоянием.
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;
}
}
В моем интеграционном тесте я объявляю свои bean-компоненты во внутреннем статическом классе конфигурации Java, как показано ниже:
@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
@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
Заранее благодарю за ответы.
2 ответа
Когда вы настраиваете bean-компонент следующим образом:
@Bean
@StepScope
public MyInterface myBean() {
return new MyInterfaceImpl();
}
Вы говорите Spring, чтобы использовать режим прокси ScopedProxyMode.TARGET_CLASS
, Однако, возвращая MyInterface
вместо MyInterfaceImpl
прокси имеет видимость только в методах на MyInterface
, Это не позволяет Spring Batch найти методы на MyInterfaceImpl
которые были аннотированы аннотациями слушателя, такими как @BeforeStep
, Правильный способ настроить это вернуть MyInterfaceImpl
на ваш метод конфигурации, как показано ниже:
@Bean
@StepScope
public MyInterfaceImpl myBean() {
return new MyInterfaceImpl();
}
Мы добавили сообщение журнала предупреждений при запуске, которое указывает, что, когда мы ищем аннотированные методы прослушивателя, если объект проксируется, а цель является интерфейсом, мы не сможем найти методы в реализующем классе с аннотациями на их.
как предлагает pojo-guy Решение состоит в том, чтобы реализовать StepExecutionListener и Override beforeStep, чтобы установить stepExecution
@Override
public void beforeStep(StepExecution stepExecution) {
this.stepExecution = stepExecution;
}