当前位置: 首页 > 知识库问答 >
问题:

Spring BootSpring批次@StepScope =没有代理的豆子?

长孙文栋
2023-03-14

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

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

主批处理配置文件:

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

患者ItemWriter代码:

@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,如果没有它,作业将在启动时运行。

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

如有任何帮助,将不胜感激。提前感谢。

回复gal的更新

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个答案

施俊远
2023-03-14

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

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

当你想得到一个reader实例来构建step的实例时,你不能使用like reader(null)。您应该将读取器设置为步骤的参数,然后Spring容器将注入读取器实例。这是正确的代码:

@Bean
public Step step(ItemReader<Patient> reader){
    return stepBuilders.get("step")
            .<Patient,Patient>chunk(1)
            .reader(reader)
            .writer(writer)
            .build();
}
段宏毅
2023-03-14

我想问题是您在 Spring 之外创建阅读器并在执行此操作时向其传递空值:

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

您应该执行类似的操作才能使用Spring功能,例如后期绑定:

@Autowired
private ItemReader<Patient> reader;

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

就像你为你的作家所做的那样。

 类似资料:
  • 我试图获得一个关于Spring代理如何工作的高级视图。为什么我应该在我的应用程序中使用“API接口&Impl bean”模式或者只使用“bean类”模式。我读了不少这样的答案,但它们都很古老,我相信大多数与Java7和Spring3.x相关。我想知道在2020年,Java11+与Spring5.x(Spring Boot2.x)仍然有什么关系。是否有“最佳做法”可循? 此外,由于的可用性,如果我有

  • 我正在使用Spring Batch Admin从我的主项目的批处理模块中启动批处理。 在Spring batch Admin项目的依赖项中,将批处理模块编译为JAR addedstrong文本,如下所示: 因为我在一个reader类上添加了“@stepscope”,所以在部署Spring Batch Admin时,我会遇到以下错误 但是我没有找到如何使用类似的声明来防止Spring Batch A

  • 我有一个定制的作家,它很好用;但是,我想通过JobParameters来设置我的输出文件的名称,而不是在我的配置中使用固定的字符串。为了做到这一点,我添加了@stepscope注释和参数,就像我对ItemReader所做的那样。 ItemWriter声明 步骤声明 这段代码不起作用,我得到一个WriterNotOpenException是因为我使用FlatFileItemWriter作为委托。 当

  • 我尝试了不同的版本,我总是得到同样的错误。

  • 前言 sender的run 调用done方法,并传入TimeoutException

  • 我正在使用Spring批处理设置一个作业服务器。我的JdbcCursorItemReader需要配置sql,该sql在每个作业运行的基础上进行更改。因为sql发生了变化,所以我希望阅读器具有@stepscope,这样我就不需要担心sql的状态性了。 所以我设置了这样一个类: 我在整个服务器上使用基于Java的配置。ItemReader的一个实例的bean如下所示: 启动服务器并运行Spring批处

  • 我怎么才能修好这个?