Spring 如果应用实现了 CommandLineRunner,批量重试在 Java 中不起作用
Spring Batch retry doesn't work in Java if the app implements the CommandLineRunner
作为参考 I checked in the code of the following spring batch reader retry example https://github.com/atulkulkarni18/spring-batch-reader-retry 并且重试功能按预期工作。
然而,实施 CommandLineRunner(如下面的代码所示),重试功能不起作用...有人可以建议吗?
@SpringBootApplication
@EnableBatchProcessing
@Data
@NoArgsConstructor
@AllArgsConstructor
@EnableRetry
public class Stack56170179Application implements CommandLineRunner {
@Autowired
JobLauncher jobLauncher;
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
public static void main(String[] args) {
SpringApplication.run(Stack56170179Application.class, args);
}
@Bean
public Job job() {
return jobs.get("myJob").start(step1()).build();
}
@Bean
public Step step1() {
return steps.get("step1").<String, String>chunk(1).reader(myReader())
.processor(myProcessor())
.writer(myWriter())
.build();
}
@Override
public void run(String... args) throws Exception {
JobParameters params = new JobParametersBuilder()
.addString("JobID", String.valueOf(System.currentTimeMillis()))
.toJobParameters();
jobLauncher.run(job(), params);
}
@Bean
@StepScope
public MyReader myReader() {
return new MyReader();
}
@Bean
@StepScope
public MyProcessor myProcessor() {
return new MyProcessor();
}
@Bean
@StepScope
public MyWriter myWriter() {
return new MyWriter();
}
}
public class MyReader implements ItemReader<String> {
private long count;
private long retryCount;
@Override
@Retryable(include = { MyException.class }, maxAttempts = 5)
public String read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
final long value = count;
System.out.println("MyReader : " + value);
if (value == 3 && retryCount <= 2) {
retryCount++;
System.out.println("****");
Thread.sleep(500);
throw new MyException();
}
if (value < 5) {
count++;
Thread.sleep(500);
return String.valueOf(value);
} else {
return null;
}
}
}
public class MyProcessor implements ItemProcessor<String, String> {
@Override
public String process(final String arg0) throws Exception {
System.out.println("MyProcessor : " + arg0);
return arg0;
}
}
public class MyWriter implements ItemWriter<String> {
@Override
public void write(final List<? extends String> arg0) throws Exception {
System.out.println("MyWriter : " + arg0);
}
}
public class MyException extends Exception {
private static final long serialVersionUID = 1L;
}
预期输出:
MyReader : 0
MyProcessor : 0
MyWriter : [0]
MyReader : 1
MyProcessor : 1
MyWriter : [1]
MyReader : 2
MyProcessor : 2
MyWriter : [2]
MyReader : 3
****
MyReader : 3
****
MyReader : 3
****
MyReader : 3
MyProcessor : 3
MyWriter : [3]
MyReader : 4
MyProcessor : 4
MyWriter : [4]
MyReader : 5
问题不在于 CommandLineRunner
,而在于 MyReader
对象。您已经创建了普通对象,但它不是 Spring 托管的。
尝试像
这样创建 spring 托管 bean
@Bean
@StepScope
public MyReader myReader() {
return new MyReader();
}
并在方法 step1()
中使用 myReader()
而不是 new MyReader()
。
希望对您有所帮助!
作为参考
@SpringBootApplication
@EnableBatchProcessing
@Data
@NoArgsConstructor
@AllArgsConstructor
@EnableRetry
public class Stack56170179Application implements CommandLineRunner {
@Autowired
JobLauncher jobLauncher;
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
public static void main(String[] args) {
SpringApplication.run(Stack56170179Application.class, args);
}
@Bean
public Job job() {
return jobs.get("myJob").start(step1()).build();
}
@Bean
public Step step1() {
return steps.get("step1").<String, String>chunk(1).reader(myReader())
.processor(myProcessor())
.writer(myWriter())
.build();
}
@Override
public void run(String... args) throws Exception {
JobParameters params = new JobParametersBuilder()
.addString("JobID", String.valueOf(System.currentTimeMillis()))
.toJobParameters();
jobLauncher.run(job(), params);
}
@Bean
@StepScope
public MyReader myReader() {
return new MyReader();
}
@Bean
@StepScope
public MyProcessor myProcessor() {
return new MyProcessor();
}
@Bean
@StepScope
public MyWriter myWriter() {
return new MyWriter();
}
}
public class MyReader implements ItemReader<String> {
private long count;
private long retryCount;
@Override
@Retryable(include = { MyException.class }, maxAttempts = 5)
public String read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
final long value = count;
System.out.println("MyReader : " + value);
if (value == 3 && retryCount <= 2) {
retryCount++;
System.out.println("****");
Thread.sleep(500);
throw new MyException();
}
if (value < 5) {
count++;
Thread.sleep(500);
return String.valueOf(value);
} else {
return null;
}
}
}
public class MyProcessor implements ItemProcessor<String, String> {
@Override
public String process(final String arg0) throws Exception {
System.out.println("MyProcessor : " + arg0);
return arg0;
}
}
public class MyWriter implements ItemWriter<String> {
@Override
public void write(final List<? extends String> arg0) throws Exception {
System.out.println("MyWriter : " + arg0);
}
}
public class MyException extends Exception {
private static final long serialVersionUID = 1L;
}
预期输出:
MyReader : 0
MyProcessor : 0
MyWriter : [0]
MyReader : 1
MyProcessor : 1
MyWriter : [1]
MyReader : 2
MyProcessor : 2
MyWriter : [2]
MyReader : 3
****
MyReader : 3
****
MyReader : 3
****
MyReader : 3
MyProcessor : 3
MyWriter : [3]
MyReader : 4
MyProcessor : 4
MyWriter : [4]
MyReader : 5
问题不在于 CommandLineRunner
,而在于 MyReader
对象。您已经创建了普通对象,但它不是 Spring 托管的。
尝试像
@Bean
@StepScope
public MyReader myReader() {
return new MyReader();
}
并在方法 step1()
中使用 myReader()
而不是 new MyReader()
。
希望对您有所帮助!