java - jobbuilderfactory - Spring-batch @BeforeStep no funciona con @StepScope
spring boot (1)
Cuando configura un bean de la siguiente manera:
@Bean
@StepScope
public MyInterface myBean() {
return new MyInterfaceImpl();
}
Le estás diciendo a Spring que use el modo proxy ScopedProxyMode.TARGET_CLASS
. Sin embargo, al devolver MyInterface
, en lugar de MyInterfaceImpl
, el proxy solo tiene visibilidad de los métodos en MyInterface
. Esto evita que Spring Batch pueda encontrar los métodos en MyInterfaceImpl
que se han anotado con las anotaciones del oyente como @BeforeStep
. La forma correcta de configurar esto es devolver MyInterfaceImpl
en su método de configuración como se muestra a continuación:
@Bean
@StepScope
public MyInterfaceImpl myBean() {
return new MyInterfaceImpl();
}
Hemos agregado un mensaje de registro de advertencia en el inicio que señala, mientras buscamos los métodos de escucha anotados, si el objeto tiene un proxy y el objetivo es una interfaz, no podremos encontrar métodos en la clase de implementación con anotaciones en ellos.
Estoy usando Spring Batch versión 2.2.4.RELEASE Intenté escribir un ejemplo simple con ItemReader, ItemProcessor y ItemWriter con estado.
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;
}
}
En mi prueba de integración, declaro mis beans en una clase de configuración java estática interna como la siguiente:
@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());
}
}
Esta prueba pasa.
Pero tan pronto como defino mi StatefulItemReader como un bean de ámbito de paso (que es mejor para un lector con estado), el código "antes del paso" ya no se ejecuta.
...
@Bean
@StepScope
public ItemReader<String> reader() {
return new StatefulItemReader();
}
...
Y me doy cuenta del mismo problema con el procesador y mis beans de escritor.
¿Qué pasa con mi código? ¿Está relacionado con este problema resuelto: https://jira.springsource.org/browse/BATCH-1230
Todo mi proyecto de Maven con varias pruebas de JUnit se puede encontrar en GitHub: https://github.com/galak75/spring-batch-step-scope
Gracias de antemano por sus respuestas.