web-dev-qa-db-fra.com

Spring-batch @BeforeStep ne fonctionne pas avec @StepScope

J'utilise la version 2.2.4 de Spring Batch.RELEASE J'ai essayé d'écrire un exemple simple avec les beans StateReader, ItemProcessor et ItemWriter avec état.

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;
    }
}

Dans mon test d'intégration, je déclare mes beans dans une classe statique interne Java config comme celle ci-dessous:

@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());
    }
}

Ce test réussit.

Mais dès que je définis mon StatefulItemReader comme un bean à portée d'étape (ce qui est mieux pour un lecteur avec état), le code "avant l'étape" n'est plus réalisé.

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

Et je remarque le même problème avec le processeur et mes beans d'écriture.

Qu'est ce qui ne va pas avec mon code? Est-ce lié à ce problème résolu: https://jira.springsource.org/browse/BATCH-12

Tout mon projet Maven avec plusieurs tests JUnit peut être trouvé sur GitHub: https://github.com/galak75/spring-batch-step-scope

Merci d'avance pour vos réponses.

26
Géraud

Lorsque vous configurez un bean comme suit:

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

Vous dites à Spring d'utiliser le mode proxy ScopedProxyMode.TARGET_CLASS. Cependant, en renvoyant le MyInterface, au lieu du MyInterfaceImpl, le proxy n'a de visibilité que sur les méthodes du MyInterface. Cela empêche Spring Batch de trouver les méthodes sur MyInterfaceImpl qui ont été annotées avec les annotations d'écoute comme @BeforeStep. La bonne façon de configurer ceci est de retourner MyInterfaceImpl sur votre méthode de configuration comme ci-dessous:

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

Nous avons ajouté un message de journal d'avertissement au démarrage qui souligne, lorsque nous recherchons les méthodes d'écoute annotées, si l'objet est mandaté et que la cible est une interface, nous ne pourrons pas trouver de méthodes sur la classe d'implémentation avec des annotations sur leur.

38
Michael Minella