关于在Spring batch 5中从StepExecutionContext中检索JobParameters和检索变量的问题

uhry853o  于 2023-06-29  发布在  Spring
关注(0)|答案(1)|浏览(111)

我正在尝试测试我用Springbatch 5.0创建的编写器和阅读器。我注意到,当我将@Value(“#{jobParameters ['filename']}”添加到我的writer甚至我的处理器时,我得到了这个错误消息,“No Scope registered for scope name 'step'”。但是,如果我把它移走,它就起作用了。不确定这与Springbatch 5的更改有什么关系,所以把它带到这里寻求帮助。
这是我的:
application-test.properties

outputLocation=src/test/resources/output
inputLocation=src/test/resources/input
inputFileName=employee.txt
outputFileName=employees.csv

EmployeeRecord.java

@Getter
@Setter
@NoArgsConstructor
public class EmployeeRecord implements Serializable {
    private Long id;
    private String employeeNumber;
    private String firstName;
    private String lastName;
    private String role;
    private String salary;
    private LocalDateTime joiningDate;
    private String fileName;

    @Override
    public String toString() {
        return new StringJoiner(", ", EmployeeRecord.class.getSimpleName() + "[", "]")
                .add("id=" + id)
                .add("employeeNumber='" + employeeNumber + "'")
                .add("firstName='" + firstName + "'")
                .add("lastName='" + lastName + "'")
                .add("role='" + role + "'")
                .add("salary='" + salary + "'")
                .add("joiningDate=" + joiningDate + "'")
                .add("fileName=" + fileName)
                .toString();
    }

}

Employee.java

@Getter
@Setter
@NoArgsConstructor
public class Employee implements Serializable {
    private Long id;
    private String employeeNumber;
    private String firstName;
    private String lastName;
    private String role;
    private String salary;
    private LocalDateTime joiningDate;

    @Override
    public String toString() {
        return new StringJoiner(", ", Employee.class.getSimpleName() + "[", "]")
                .add("id=" + id)
                .add("employeeNumber='" + employeeNumber + "'")
                .add("firstName='" + firstName + "'")
                .add("lastName='" + lastName + "'")
                .add("role='" + role + "'")
                .add("salary='" + salary + "'")
                .add("joiningDate=" + joiningDate)
                .toString();
    }

}

ReaderWriter.java

@Slf4j
@Configuration
public class ReaderWriter {
    @Value("${inputLocation}")
    private String inputLocation;
    @Value("${outputLocation}")
    private String outputLocation;
    @Value("${inputFileName}")
    private String inputFileName;
    @Value("${outputFileName}")
    private String outputFileName;
    @Autowired
    private FieldSetMapper<Employee> employeeFieldSetMapper;

    @Bean
    @StepScope
    public FlatFileItemReader<Employee> flatFileItemReader() {
        log.info("reading from -> {} ", inputLocation);
        log.debug("reading from -> {} ", inputLocation);
        return new FlatFileItemReaderBuilder<Employee>()
                .name("employeeItemReader")
                .resource(new FileSystemResource(inputLocation+"/"+inputFileName))
                .linesToSkip(1)
                .fieldSetMapper(employeeFieldSetMapper)
                .lineMapper(lineMapper())
                .build();
    }

    @Bean
    @StepScope
    public FlatFileItemWriter<EmployeeRecord> flatFileItemWriter(@Value("#{jobParameters['filename']}") String fileName) throws MalformedURLException {
        log.info("writing to -> {} ", outputLocation);
        String file = outputLocation+"/"+outputFileName;
        return new FlatFileItemWriterBuilder<EmployeeRecord>()
                .name("employeeItemWriter")
                .resource(new FileSystemResource(file))
                .lineAggregator(lineAggregator())
                .headerCallback(header -> header.write(fileName))
                .build();
    }

    private DefaultLineMapper<Employee> lineMapper() {
        DefaultLineMapper<Employee> lineMapper = new DefaultLineMapper<>();
        lineMapper.setLineTokenizer(tokenizer());
        lineMapper.setFieldSetMapper(employeeFieldSetMapper);
        return lineMapper;
    }

    private LineTokenizer tokenizer() {
        DelimitedLineTokenizer delimitedLineTokenizer = new DelimitedLineTokenizer();
        delimitedLineTokenizer.setDelimiter("|");
        delimitedLineTokenizer.setNames("id", "employeeNumber", "firstName", "lastName", "role", "salary", "joiningDate");
        return delimitedLineTokenizer;
    }

    private LineAggregator<EmployeeRecord> lineAggregator() {
        DelimitedLineAggregator<EmployeeRecord> delimitedLineAggregator = new DelimitedLineAggregator<>();
        delimitedLineAggregator.setDelimiter(",");
        delimitedLineAggregator.setFieldExtractor(extractor());
        return delimitedLineAggregator;
    }

    private FieldExtractor<EmployeeRecord> extractor() {
        BeanWrapperFieldExtractor<EmployeeRecord> beanWrapperFieldExtractor = new BeanWrapperFieldExtractor<>();
        beanWrapperFieldExtractor.setNames(new String[]{
                "id",
                "employeeNumber",
                "firstName",
                "lastName",
                "role",
                "salary",
                "joiningDate",
                "fileName"
        });
        beanWrapperFieldExtractor.afterPropertiesSet();
        return beanWrapperFieldExtractor;
    }

}

EmployeeProcessor.java

@StepScope
@Slf4j
@Component("employeeProcessor")
public class EmployeeProcessor implements ItemProcessor<Employee, EmployeeRecord> {
    @Value("#{jobParameters['filename']}")
    private String fileName;

    @Override
    public EmployeeRecord process(Employee employee) throws Exception {
        Long id = employee.getId();
        String employeeNumber = employee.getEmployeeNumber();
        String firstName = employee.getFirstName();
        String lastName = employee.getLastName();
        String role = employee.getRole();
        String salary = employee.getSalary();
        LocalDateTime joiningDate = employee.getJoiningDate();

        EmployeeRecord employeeProcessed = new EmployeeRecord();
        employeeProcessed.setId(id);
        employeeProcessed.setFirstName(firstName);
        employeeProcessed.setLastName(lastName);
        employeeProcessed.setEmployeeNumber(employeeNumber);
        employeeProcessed.setSalary(salary);
        employeeProcessed.setJoiningDate(joiningDate);
        employeeProcessed.setFileName(fileName);
        employeeProcessed.setRole(role);
        return employeeProcessed;
    }
}

EmployeeStepListener.java

@Slf4j
@Component("employeeStepListener")
public class EmployeeStepListener implements StepExecutionListener {
    @Override
    public void beforeStep(StepExecution stepExecution) {
        log.info("Called beforeStep -> {} ", stepExecution.getJobParameters().getString("filename"));
    }

    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {
        log.info("Called afterStep");
        return stepExecution.getExitStatus();
    }
}

BatchConfig.java

@Slf4j
@Configuration
public class BatchConfig {

    @Bean
    public Job employeeJob(JobRepository jobRepository,@Qualifier("employeeStep") Step employeeStep,@Qualifier("employeeJobListener") JobExecutionListener listener) {
        log.debug("inside employeeJob");
        return new JobBuilder("employeeJob", jobRepository)
                .incrementer(new RunIdIncrementer())
                .listener(listener)
                .start(employeeStep)
                .build();
    }

}

EmployeeFieldSetMapper.java

@Component("employeeFieldSetMapper")
public class EmployeeFieldSetMapper implements FieldSetMapper<Employee> {

    @Override
    public Employee mapFieldSet(FieldSet fieldSet) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy:HH:mm:ss");
        Employee employee = new Employee();
        employee.setId(fieldSet.readLong("id"));
        employee.setFirstName(fieldSet.readString("firstName"));
        employee.setLastName(fieldSet.readString("lastName"));
        employee.setRole(fieldSet.readString("role"));
        employee.setSalary(fieldSet.readString("salary"));
        String dateTime = fieldSet.readString("joiningDate");
        employee.setJoiningDate(LocalDateTime.parse(dateTime,dateTimeFormatter));

        return employee;
    }
}

EmployeeCommonConfig.java

@Configuration
public class EmployeeCommonConfig {
    @Qualifier("batchDataSource")
    @Autowired
    private DataSource dataSource;

    @Bean
    protected JobRepository jobRepository() throws Exception {
        JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
        factory.setDataSource(dataSource);
        factory.setTransactionManager(new DataSourceTransactionManager(dataSource));
        factory.setIncrementerFactory(new DefaultDataFieldMaxValueIncrementerFactory(dataSource));
        factory.setIsolationLevelForCreateEnum(Isolation.READ_COMMITTED);
        factory.setTablePrefix("BATCH_");
        factory.afterPropertiesSet();

        return factory.getObject();
    }

    @Bean
    protected JobLauncher jobLauncher() throws Exception {
        TaskExecutorJobLauncher launcher = new TaskExecutorJobLauncher();
        launcher.setJobRepository(jobRepository());
        launcher.setTaskExecutor(new SyncTaskExecutor());
        launcher.afterPropertiesSet();
        return launcher;
    }
}

StepConfig.java

@Slf4j
@Configuration
public class StepConfig {
    @Value("${inputLocation}")
    private String inputLocation;
    @Value("${outputLocation}")
    private String outputLocation;
    @Value("${inputFileName}")
    private String inputFileName;
    @Value("${outputFileName}")
    private String outputFileName;
    @Autowired
    private ItemReader<Employee> flatFileItemReader;
    @Autowired
    private ItemWriter<EmployeeRecord> flatFileItemWriter;
    @Autowired
    private EmployeeProcessor employeeProcessor;

    @Bean
    public Step employeeStep(PlatformTransactionManager transactionManager, JobRepository jobRepository) {
        log.debug("debug: in employeeStep");
        log.info("info: in employeeStep");
        log.debug("inputLocation -> {} ", inputLocation);
        log.debug("outputLocation -> {} ", outputLocation);
        return new StepBuilder("employeeStep",jobRepository)
                .<Employee,EmployeeRecord>chunk(2,transactionManager)
                .reader(flatFileItemReader)
                .processor(employeeProcessor)
                .writer(flatFileItemWriter)
                .listener("employeeStepListener")
                .build();
    }

}

DbConfig.java

@Configuration
public class DbConfig {

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean("batchDataSource")
    public HikariDataSource dataSource() {
        return DataSourceBuilder
                .create()
                .type(HikariDataSource.class)
                .build();
    }

}

DatabaseTestConfig.java

@Configuration
public class DatabaseTestConfig {

    @Bean(name = "batchDataSource")
    public DataSource batchDataSource() {
        return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .build();
    }
    @Bean(name = "batchTransactionManager")
    public DataSourceTransactionManager batchTransactionManager() {
        return new DataSourceTransactionManager(batchDataSource());
    }
}

EmployeeTestConfig.java

@Configuration
@ComponentScan(basePackages = {"com.test"},
        excludeFilters = {@ComponentScan .Filter (type = FilterType.ASSIGNABLE_TYPE, classes = {BatchConfig.class})})
public class EmployeeTestConfig {
    @Autowired
    private JobRepository jobRepository;

    @Bean
    public Job employeeJob(@Qualifier("employeeStep") Step employeeStep) {
        return new JobBuilder("employeeJob", jobRepository)
                .incrementer(new RunIdIncrementer())
                .start(employeeStep)
                .build();
    }
}

EmployeeExecutionTest.java

@SpringBatchTest
@SpringJUnitConfig({EmployeeTestConfig.class, DatabaseTestConfig.class})
@TestPropertySource(locations = "classpath:application-test.properties")
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:data-h2.sql")
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class EmployeeExecutionTest {
    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;
    @Test
    public void employeeJobExecutionTest() throws Exception {
        JobParameters jobParameter = new JobParametersBuilder()
                .addString("filename", "testname").toJobParameters();
        JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameter);
        assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());

    }
}

堆栈跟踪:

2023.03.22 20:41:29.930 [INFO] org.springframework.batch.core.job.SimpleStepHandler: Executing step: [employeeStep] 2023.03.22 20:41:29.937 [ERROR] org.springframework.batch.core.step.AbstractStep: Encountered an error executing step employeeStep in job employeeJob java.lang.IllegalStateException: No Scope registered for scope name 'step' at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:359) ~[spring-beans-6.0.6.jar:6.0.6] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.0.6.jar:6.0.6] at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35) ~[spring-aop-6.0.6.jar:6.0.6] at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:686) ~[spring-aop-6.0.6.jar:6.0.6] at org.springframework.batch.item.file.FlatFileItemWriter$$SpringCGLIB$$0.open() ~[spring-batch-infrastructure-5.0.1.jar:5.0.1] at org.springframework.batch.item.support.CompositeItemStream.open(CompositeItemStream.java:124) ~[spring-batch-infrastructure-5.0.1.jar:5.0.1] at org.springframework.batch.core.step.tasklet.TaskletStep.open(TaskletStep.java:293) ~[spring-batch-core-5.0.1.jar:5.0.1] at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:224) ~[spring-batch-core-5.0.1.jar:5.0.1] at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:153) ~[spring-batch-core-5.0.1.jar:5.0.1] at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:417) ~[spring-batch-core-5.0.1.jar:5.0.1] at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:132) ~[spring-batch-core-5.0.1.jar:5.0.1] at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:316) ~[spring-batch-core-5.0.1.jar:5.0.1] at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:157) ~[spring-batch-core-5.0.1.jar:5.0.1] at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-6.0.6.jar:6.0.6] at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:148) ~[spring-batch-core-5.0.1.jar:5.0.1] at org.springframework.batch.core.launch.support.TaskExecutorJobLauncher.run(TaskExecutorJobLauncher.java:70) ~[spring-batch-core-5.0.1.jar:5.0.1] at org.springframework.batch.test.JobLauncherTestUtils.launchJob(JobLauncherTestUtils.java:146) ~[spring-batch-test-5.0.1.jar:5.0.1] at com.codi.springbatch.springbatchapp.EmployeeExecutionTest.employeeJobExecutionTest(EmployeeExecutionTest.java:27) ~[test-classes/:?] at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[?:?] at java.lang.reflect.Method.invoke(Method.java:578) ~[?:?] at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) ~[junit-platform-commons-1.9.2.jar:1.9.2] at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) ~[junit-jupiter-engine-5.9.2.jar:5.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) ~[junit-platform-engine-1.9.2.jar:1.9.2] at java.util.ArrayList.forEach(ArrayList.java:1511) ~[?:?] at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) ~[junit-platform-engine-1.9.2.jar:1.9.2] at java.util.ArrayList.forEach(ArrayList.java:1511) ~[?:?] at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) ~[junit-platform-engine-1.9.2.jar:1.9.2] at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220) ~[junit-platform-launcher-1.3.1.jar:1.3.1] at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188) ~[junit-platform-launcher-1.3.1.jar:1.3.1] at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202) ~[junit-platform-launcher-1.3.1.jar:1.3.1] at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181) ~[junit-platform-launcher-1.3.1.jar:1.3.1] at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128) ~[junit-platform-launcher-1.3.1.jar:1.3.1] at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:150) ~[surefire-junit-platform-2.22.2.jar:2.22.2] at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:124) ~[surefire-junit-platform-2.22.2.jar:2.22.2] at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:384) ~[surefire-booter-2.22.2.jar:2.22.2] at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:345) ~[surefire-booter-2.22.2.jar:2.22.2] at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:126) ~[surefire-booter-2.22.2.jar:2.22.2] at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:418) ~[surefire-booter-2.22.2.jar:2.22.2] 2023.03.22 20:41:29.948 [INFO] org.springframework.batch.core.step.AbstractStep: Step: [employeeStep] executed in 16ms 2023.03.22 20:41:29.952 [ERROR] org.springframework.batch.core.step.AbstractStep: Exception while closing step execution resources in step employeeStep in job employeeJob java.lang.IllegalStateException: No Scope registered for scope name 'step'

3qpi33ja

3qpi33ja1#

这与Spring Batch 5无关。我无法运行您的示例并重现该问题,但该错误意味着在步骤范围外调用了步骤范围内的组件(即在步骤运行时未调用)。
关于你分享的代码的另一个注意事项。我看到您在步骤执行上下文中添加了TOTAL_RECORDS属性(即在方法StepExecutionListener#afterStep中),但在项目编写器bean定义编写器@Value("#stepExecutionContext['TOTAL_RECORDS']}") long recordCount中阅读它。这是不正确的,因为在侦听器的afterStep方法之前调用了项编写器。

编辑:我无法重现共享代码的问题。下面是一个最小的完全可验证的例子:

文件pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>so75784914</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>so75784914</name>
    <description>Demo project for SO 75784914</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-core</artifactId>
            <version>5.0.1</version>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
            <version>2.1.214</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.6</version>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.9.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-test</artifactId>
            <version>5.0.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.1</version>
                <configuration>
                    <release>17</release>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

文件src/main/java/com/example/so75784914/JobConfig.java

package com.example.so75784914;

import java.util.Arrays;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.batch.item.file.builder.FlatFileItemWriterBuilder;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.jdbc.support.JdbcTransactionManager;

@Configuration
@EnableBatchProcessing
public class JobConfig {

    @Bean
    public ListItemReader<Person> itemReader() {
        List<Person> items = Arrays.asList(
                new Person(1, "foo1")
                , new Person(2, "foo2")
                , new Person(3, "bar1")
                , new Person(4, "bar2")
        );
        return new ListItemReader<>(items);
    }

    @Bean
    @StepScope
    public FlatFileItemWriter<Person> itemWriter(@Value("#{jobParameters['fileName']}") String fileName) {
        return new FlatFileItemWriterBuilder<Person>()
                .name("personItemWriter")
                .resource(new FileSystemResource(fileName))
                .delimited()
                .names("id", "name")
                .build();
    }

    @Bean
    public Step step(JobRepository jobRepository, JdbcTransactionManager transactionManager,
                     ItemReader<Person> reader, ItemWriter<Person> writer) {
        return new StepBuilder("step", jobRepository)
                .<Person, Person>chunk(2, transactionManager)
                .reader(reader)
                .writer(writer)
                .build();
    }

    @Bean
    public Job job(JobRepository jobRepository, Step step) {
        return new JobBuilder("job", jobRepository)
                .start(step)
                .build();
    }

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .addScript("/org/springframework/batch/core/schema-h2.sql")
                .build();
    }

    @Bean
    public JdbcTransactionManager transactionManager(DataSource dataSource) {
        return new JdbcTransactionManager(dataSource);
    }

    public static void main(String[] args) throws Exception {
        ApplicationContext context = new AnnotationConfigApplicationContext(JobConfig.class);
        JobLauncher jobLauncher = context.getBean(JobLauncher.class);
        Job job = context.getBean(Job.class);
        JobParameters jobParameters = new JobParametersBuilder()
                .addString("fileName", "persons.csv")
                .toJobParameters();
        jobLauncher.run(job, jobParameters);
    }

    public static class Person {
        private int id;
        private String name;

        public Person() {
        }

        public Person(int id, String name) {
            this.id = id;
            this.name = name;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String toString() {
            return "Person{id=" + id + ", name='" + name + '\'' + '}';
        }
    }

}

文件src/test/java/com/example/so75784914/So75784914ApplicationTests.java

package com.example.so75784914;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.batch.test.context.SpringBatchTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

@SpringBatchTest
@SpringJUnitConfig({JobConfig.class})

class So75784914ApplicationTests {

    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;

    @Test
    public void employeeJobExecutionTest() throws Exception {
        JobParameters jobParameter = new JobParametersBuilder()
                .addString("fileName", "output.csv")
                .toJobParameters();
        JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameter);
        Assertions.assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());

    }

}

此示例使用一个步骤范围的项编写器,其中输出文件名是从作业参数配置的。当我运行测试时,输出文件被正确写入,没有任何问题。示例使用Spring Batch版本5.0.1,如问题中所示。

相关问题