首页 文章

Spring Boot Spring Batch @StepScope =没有Proxied Beans?

提问于
浏览
3

我目前正在为应用程序设置文件导入服务,这将允许用户通过REST上传csv文件 . 作业的导入将通过Spring Batch完成,因为这些作业可能会根据未来的处理要求和检查长时间运行 .

根据我的说法,以下设置对于Spring Boot和Spring Batch是正确的,但代码将无法编译 .

主BatchConfiguration文件:

@Configuration
@EnableBatchProcessing
public class PatientBatchConfiguration {

    @Autowired
    private JobBuilderFactory jobBuilders;

    @Autowired
    private StepBuilderFactory stepBuilders;

    @Autowired
    private PatientFieldSetMapper fieldSetMapper;

    @Autowired
    private PatientItemWriter writer;

    @Bean
    public Job importPatientsFromUpload(){
        return jobBuilders.get("importPatientsFromUpload")
                .start(step())
                .build();
    }

    @Bean
    public Step step(){
        return stepBuilders.get("step")
                .<Patient,Patient>chunk(1)
                .reader(reader(null))
                .writer(writer)
                .build();
    }

    @Bean
    @StepScope
    public ItemReader<Patient> reader(@Value("#{jobParameters['fileName']}") String filePath) {
        FlatFileItemReader<Patient> itemReader = new FlatFileItemReader<Patient>();
        itemReader.setLineMapper(lineMapper());
        itemReader.setResource(new FileSystemResource(filePath));
        return itemReader;
    }

    private LineMapper<Patient> lineMapper() {
        DefaultLineMapper<Patient> lineMapper = new DefaultLineMapper<Patient>();
        DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
        lineTokenizer.setNames(new String[]{"name","surname","idNumber","dob", "email", "cell"});
        lineMapper.setLineTokenizer(lineTokenizer);
        lineMapper.setFieldSetMapper(fieldSetMapper);
        return lineMapper;
    }

}

FieldSetMapper代码:

@Component
public class PatientFieldSetMapper implements FieldSetMapper<Patient> {

    @Override
    public Patient mapFieldSet(FieldSet fieldSet) throws BindException {

        if(fieldSet == null){
            return null;
        }

        Patient patient = new Patient();
        patient.setName(fieldSet.readString("name"));
        patient.setSurname(fieldSet.readString("surname"));
        patient.setIdNo(fieldSet.readString("idNumber"));
        patient.setDob(0L);
        patient.setEmail(fieldSet.readString("email"));
        patient.setCell(fieldSet.readString("cell"));

        return patient;
    }
}

PatientItemWriter代码:

@Component
public class PatientItemWriter implements ItemWriter<Patient> {

    @Autowired
    PatientRepository patientRepository;

    @Override
    public void write(List<? extends Patient> list) throws Exception {

        for(Patient patient: list) {
            patientRepository.save(patient);
        }
    }
}

堆栈跟踪:

Caused by: java.lang.IllegalArgumentException: Path must not be null
    at org.springframework.util.Assert.notNull(Assert.java:115) ~[spring-core-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.core.io.FileSystemResource.<init>(FileSystemResource.java:75) ~[spring-core-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at com.example.batch.patient.PatientBatchConfiguration.reader(PatientBatchConfiguration.java:59) ~[classes/:na]
    at com.example.batch.patient.PatientBatchConfiguration.step(PatientBatchConfiguration.java:49) ~[classes/:na]

最后是application.properties文件

spring.batch.job.enabled=false

spring.batch.job.enabled = false位于属性文件中的原因是,在用户上传文件后,将从控制器调用作业importPatientsFromUpload,如果没有它,则作业将在启动时运行 .

我遇到的问题是FileSystemResource无法创建,因为路径不能为null . 但是我知道只要用@StepScope注释一个方法,就会创建一个代理bean,这将允许我使用在运行时传递的jobParameter来创建一个新的文件系统资源 . 我在网上看到了各种使用jobParameters的例子,但由于某种原因,bean似乎没有正确创建 . 我不确定这是否与我使用Spring Boot或其他错误有关 .

任何帮助,将不胜感激 . 提前致谢 .

UPDATE in Reply to Gaël

PatientItemReader代码:

@Component
@StepScope
public class PatientItemReader implements ItemReader<Patient> {

    @Autowired
    private PatientFieldSetMapper fieldSetMapper;

    private FlatFileItemReader<Patient> itemReader;

    @Override
    public Patient read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
        return itemReader.read();
    }

    public PatientItemReader(@Value("#{jobParameters[filePath]}") String filePath) {
            itemReader = new FlatFileItemReader<Patient>();
            DefaultLineMapper<Patient> lineMapper = new DefaultLineMapper<Patient>();
            DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
            lineTokenizer.setNames(new String[]{"name","surname","idNumber","dob", "email", "cell"});
            lineMapper.setLineTokenizer(lineTokenizer);
            lineMapper.setFieldSetMapper(fieldSetMapper);
            itemReader.setLineMapper(lineMapper);
            itemReader.setResource(new FileSystemResource(filePath));
    }
}

2 回答

  • 0

    我想问题是你在Spring之外创建了读者并在执行此操作时传递了 null 值:

    @Bean
    public Step step(){
        ...
        .reader(reader(null))
        ...
    }
    

    你应该做这样的事情,以便使用像后期绑定这样的Spring功能:

    @Autowired
    private ItemReader<Patient> reader;
    
    @Bean
    public Step step(){
        ...
        .reader(reader)
        ...
    }
    

    正如你为作家所做的那样 .

  • 2

    当你使用@Bean时,这意味着你希望Spring容器来管理你的实例 . 所以,在这一步:

    @Bean
    public Step step() {...}
    

    如果要获取读者实例来构建步骤实例,则不能使用 reader(null) . 您应该将阅读器设置为步骤的参数,然后Spring容器将注入阅读器实例 . 这是严格的代码:

    @Bean
    public Step step(ItemReader<Patient> reader){
        return stepBuilders.get("step")
                .<Patient,Patient>chunk(1)
                .reader(reader)
                .writer(writer)
                .build();
    }
    

相关问题