5. 错误处理
5.1 错误处理概述
默认情况下,当任务出现异常时,SpringBatch会结束任务。
当使用相同的参数重启任务时,SpringBatch会执行未被执行的剩余任务。
ErrorDemo.java(模拟上述情况,留意 ! 的部分)
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
@Configuration
@EnableBatchProcessing
public class ErrorDemo {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Job errorDemoJob() {
return jobBuilderFactory.get("errorDemoJob")
.start(errorStep1())
.next(errorStep2())
.build();
}
@Bean
public Step errorStep1() {
return stepBuilderFactory.get("errorStep1")
.tasklet(null)
.build();
}
@Bean
public Step errorStep2() {
return stepBuilderFactory.get("errorStep2")
.tasklet(null)
.build();
}
@Bean
@StepScope
public Tasklet errorHandling() {
return new Tasklet() {
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
Map<String, Object> stepExecutionContext =
chunkContext.getStepContext().getStepExecutionContext();
// (!)
if (stepExecutionContext.containsKey("SUCCESS")) {
System.out.println("The next run will succeed");
return RepeatStatus.FINISHED;
} else {
System.out.println("The first run will fail");
chunkContext.getStepContext().getStepExecution().getExecutionContext()
.put("SUCCESS", true);
throw new RuntimeException("Oops! Error!");
}
}
};
}
}
控制台结果(第1次执行)
2023-04-06 16:55:20.554 INFO 1068 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=errorDemoJob]] launched with the following parameters: [{}]
2023-04-06 16:55:20.582 INFO 1068 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [errorStep1]
The first run will fail
2023-04-06 16:55:20.599 ERROR 1068 --- [ main] o.s.batch.core.step.AbstractStep : Encountered an error executing step errorStep1 in job errorDemoJob
java.lang.RuntimeException: Oops! Error!
at com.sb.error.ErrorDemo$1.execute(ErrorDemo.java:67) ~[classes/:na]
at ...
2023-04-06 16:55:20.605 INFO 1068 --- [ main] o.s.batch.core.step.AbstractStep : Step: [errorStep1] executed in 22ms
2023-04-06 16:55:20.615 INFO 1068 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=errorDemoJob]] completed with the following parameters: [{}] and the following status: [FAILED] in 46ms
2023-04-06 16:55:20.618 INFO 1068 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2023-04-06 16:55:20.623 INFO 1068 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
控制台结果(第2次执行)
2023-04-06 16:56:25.923 INFO 10748 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=errorDemoJob]] launched with the following parameters: [{}]
2023-04-06 16:56:25.952 INFO 10748 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [errorStep1]
The next run will succeed
2023-04-06 16:56:25.967 INFO 10748 --- [ main] o.s.batch.core.step.AbstractStep : Step: [errorStep1] executed in 15ms
2023-04-06 16:56:25.978 INFO 10748 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [errorStep2]
The first run will fail
2023-04-06 16:56:25.988 ERROR 10748 --- [ main] o.s.batch.core.step.AbstractStep : Encountered an error executing step errorStep2 in job errorDemoJob
java.lang.RuntimeException: Oops! Error!
at com.sb.error.ErrorDemo$1.execute(ErrorDemo.java:67) ~[classes/:na]
at ...
2023-04-06 16:56:25.991 INFO 10748 --- [ main] o.s.batch.core.step.AbstractStep : Step: [errorStep2] executed in 12ms
2023-04-06 16:56:26.000 INFO 10748 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=errorDemoJob]] completed with the following parameters: [{}] and the following status: [FAILED] in 63ms
2023-04-06 16:56:26.004 INFO 10748 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2023-04-06 16:56:26.007 INFO 10748 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
控制台结果(第3次执行)
2023-04-06 16:57:33.584 INFO 13492 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=errorDemoJob]] launched with the following parameters: [{}]
2023-04-06 16:57:33.603 INFO 13492 --- [ main] o.s.batch.core.job.SimpleStepHandler : Step already complete or not restartable, so no action to execute: StepExecution: id=59, version=3, name=errorStep1, status=COMPLETED, exitStatus=COMPLETED, readCount=0, filterCount=0, writeCount=0 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0, exitDescription=
2023-04-06 16:57:33.614 INFO 13492 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [errorStep2]
The next run will succeed
2023-04-06 16:57:33.630 INFO 13492 --- [ main] o.s.batch.core.step.AbstractStep : Step: [errorStep2] executed in 16ms
2023-04-06 16:57:33.637 INFO 13492 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=errorDemoJob]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 40ms
2023-04-06 16:57:33.641 INFO 13492 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2023-04-06 16:57:33.644 INFO 13492 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
稍微解释一下:
- 第1次执行的时候 在errorStep1执行的过程中抛出了异常(但在抛异常之前会把上下文的键值对设置正确)
- 第2次执行的时候 因为errorStep1的上下文的键值对是正确的,所以errorStep1的“next run”会被成功执行 但是errorStep2的“first run”依然会失败,原因还是一样:上下文的键值对没有设置(但在抛异常之前会把上下文的键值对设置正确)
- 第3次执行的时候 因为errorStep2的上下文的键值对是正确的,所以errorStep2的“next run”会被成功执行
5.2 错误重试(Retry)
如果不想Job在被执行过程中像上述例子中一样停止,我们可以使用retry
RetryItemWriter.java
import org.springframework.batch.item.ItemWriter;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class RetryItemWriter implements ItemWriter<String> {
@Override
public void write(List<? extends String> list) throws Exception {
list.forEach(System.out::println);
}
}
public class CustomRetryException extends Exception {
public CustomRetryException() {
super();
}
public CustomRetryException(String message) {
super(message);
}
}
RetryItemProcessor.java(到ITEM_13的时候会抛异常)
import org.springframework.batch.item.ItemProcessor;
import org.springframework.stereotype.Component;
@Component
public class RetryItemProcessor implements ItemProcessor<String, String> {
private int attemptCount = 0;
@Override
public String process(String item) throws Exception {
System.out.println("processing item: " + item);
if ("ITEM_13".equals(item)) {
attemptCount++;
if (attemptCount >= 3) {
System.out.println("Retried " + attemptCount + " times and finally succeeded.");
return "ITEM_" + (Integer.parseInt(item.substring(5)) * -1);
} else {
System.out.println("Process failure * " + attemptCount + ".");
throw new CustomRetryException("Process failed and attempts: " + attemptCount);
}
} else {
return "ITEM_" + (Integer.parseInt(item.substring(5)) * -1);
}
}
}
CustomRetryException.java
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class RetryDemo {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
private ItemWriter<String> retryItemWriter;
@Autowired
private ItemProcessor<String, String> retryItemProcessor;
@Bean
public Job retryDemoJob() {
return jobBuilderFactory.get("retryDemoJob")
.start(retryDemoStep())
.build();
}
@Bean
public Step retryDemoStep() {
return stepBuilderFactory.get("retryDemoStep")
.<String, String>chunk(5)
.reader(reader())
.processor(retryItemProcessor)
.writer(retryItemWriter)
// (!)
.faultTolerant()
.retry(CustomRetryException.class)
.retryLimit(5)
.build();
}
@Bean
@StepScope
public ListItemReader<String> reader() {
List<String> items = new ArrayList<>();
for (int i = 0; i < 30; i++) {
items.add("ITEM_" + String.valueOf(i));
}
ListItemReader<String> reader = new ListItemReader<>(items);
return reader;
}
}
console
2023-04-06 17:49:42.619 INFO 25584 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=retryDemoJob]] launched with the following parameters: [{}]
2023-04-06 17:49:42.646 INFO 25584 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [retryDemoStep]
processing item: ITEM_0
processing item: ITEM_1
processing item: ITEM_2
processing item: ITEM_3
processing item: ITEM_4
ITEM_0
ITEM_-1
ITEM_-2
ITEM_-3
ITEM_-4
processing item: ITEM_5
processing item: ITEM_6
processing item: ITEM_7
processing item: ITEM_8
processing item: ITEM_9
ITEM_-5
ITEM_-6
ITEM_-7
ITEM_-8
ITEM_-9
processing item: ITEM_10
processing item: ITEM_11
processing item: ITEM_12
processing item: ITEM_13
Process failure * 1.
processing item: ITEM_10
processing item: ITEM_11
processing item: ITEM_12
processing item: ITEM_13
Process failure * 2.
processing item: ITEM_10
processing item: ITEM_11
processing item: ITEM_12
processing item: ITEM_13
Retried 3 times and finally succeeded.
processing item: ITEM_14
ITEM_-10
ITEM_-11
ITEM_-12
ITEM_-13
ITEM_-14
processing item: ITEM_15
processing item: ITEM_16
processing item: ITEM_17
processing item: ITEM_18
processing item: ITEM_19
ITEM_-15
ITEM_-16
ITEM_-17
ITEM_-18
ITEM_-19
processing item: ITEM_20
processing item: ITEM_21
processing item: ITEM_22
processing item: ITEM_23
processing item: ITEM_24
ITEM_-20
ITEM_-21
ITEM_-22
ITEM_-23
ITEM_-24
processing item: ITEM_25
processing item: ITEM_26
processing item: ITEM_27
processing item: ITEM_28
processing item: ITEM_29
ITEM_-25
ITEM_-26
ITEM_-27
ITEM_-28
ITEM_-29
2023-04-06 17:49:42.697 INFO 25584 --- [ main] o.s.batch.core.step.AbstractStep : Step: [retryDemoStep] executed in 50ms
2023-04-06 17:49:42.704 INFO 25584 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=retryDemoJob]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 71ms
2023-04-06 17:49:42.709 INFO 25584 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2023-04-06 17:49:42.712 INFO 25584 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
稍微解释一下:
核心处是
stepBuilderFactory.get("retryDemoStep")<String,String>chunk(5).reader(reader()).processor(retryItemProcessor).writer(retryItemWriter).faultTolerant().retry(CustomRetryException.class).retryLimit(5).build();
先把默认修改成可以容忍抛出异常,再设定会对特定的异常进行retry,最后设置retry的次数
5.3 错误跳过(Skip)
如果不想Job在被执行过程中遇到异常像上述例子中一样retry,我们还可以skip
SkipItemWriter.java
import org.springframework.batch.item.ItemWriter;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class SkipItemWriter implements ItemWriter<String> {
@Override
public void write(List<? extends String> list) throws Exception {
list.forEach(System.out::println);
}
}
SkipItemProcessor.java(到ITEM_13的时候会抛异常)
import com.sb.error.retry.CustomRetryException;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.stereotype.Component;
@Component
public class SkipItemProcessor implements ItemProcessor<String, String> {
private int attemptCount = 0;
@Override
public String process(String item) throws Exception {
System.out.println("processing item: " + item);
if ("ITEM_13".equals(item)) {
attemptCount++;
if (attemptCount >= 3) {
System.out.println("Retried " + attemptCount + " times and finally succeeded.");
return "ITEM_" + (Integer.parseInt(item.substring(5)) * -1);
} else {
System.out.println("Process failure * " + attemptCount + ".");
throw new CustomRetryException("Process failed and attempts: " + attemptCount);
}
} else {
return "ITEM_" + (Integer.parseInt(item.substring(5)) * -1);
}
}
}
CustomRetryException.java
public class CustomRetryException extends Exception {
public CustomRetryException() {
super();
}
public CustomRetryException(String message) {
super(message);
}
}
SkipDemo.java
import com.sb.error.retry.CustomRetryException;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableBatchProcessing
public class SkipDemo {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
private ItemWriter<String> skipItemWriter;
@Autowired
private ItemProcessor<String, String> skipItemProcessor;
@Bean
public Job skipDemoJob() {
return jobBuilderFactory.get("skipDemoJob")
.start(skipDemoStep())
.build();
}
@Bean
public Step skipDemoStep() {
return stepBuilderFactory.get("skipDemoStep")
.<String, String>chunk(5)
.reader(reader())
.processor(skipItemProcessor)
.writer(skipItemWriter)
// (!)
.faultTolerant()
.skip(CustomRetryException.class)
.skipLimit(5)
.build();
}
@Bean
@StepScope
public ListItemReader<String> reader() {
List<String> items = new ArrayList<>();
for (int i = 0; i < 30; i++) {
items.add("ITEM_" + String.valueOf(i));
}
ListItemReader<String> reader = new ListItemReader<>(items);
return reader;
}
}
console
2023-04-06 19:41:19.547 INFO 15484 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=skipDemoJob]] launched with the following parameters: [{}]
2023-04-06 19:41:19.558 INFO 15484 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [skipDemoStep]
processing item: ITEM_0
processing item: ITEM_1
processing item: ITEM_2
processing item: ITEM_3
processing item: ITEM_4
ITEM_0
ITEM_-1
ITEM_-2
ITEM_-3
ITEM_-4
processing item: ITEM_5
processing item: ITEM_6
processing item: ITEM_7
processing item: ITEM_8
processing item: ITEM_9
ITEM_-5
ITEM_-6
ITEM_-7
ITEM_-8
ITEM_-9
processing item: ITEM_10
processing item: ITEM_11
processing item: ITEM_12
processing item: ITEM_13
Process failure * 1.
processing item: ITEM_10
processing item: ITEM_11
processing item: ITEM_12
processing item: ITEM_14
ITEM_-10
ITEM_-11
ITEM_-12
ITEM_-14
processing item: ITEM_15
processing item: ITEM_16
processing item: ITEM_17
processing item: ITEM_18
processing item: ITEM_19
ITEM_-15
ITEM_-16
ITEM_-17
ITEM_-18
ITEM_-19
processing item: ITEM_20
processing item: ITEM_21
processing item: ITEM_22
processing item: ITEM_23
processing item: ITEM_24
ITEM_-20
ITEM_-21
ITEM_-22
ITEM_-23
ITEM_-24
processing item: ITEM_25
processing item: ITEM_26
processing item: ITEM_27
processing item: ITEM_28
processing item: ITEM_29
ITEM_-25
ITEM_-26
ITEM_-27
ITEM_-28
ITEM_-29
2023-04-06 19:41:19.608 INFO 15484 --- [ main] o.s.batch.core.step.AbstractStep : Step: [skipDemoStep] executed in 50ms
2023-04-06 19:41:19.616 INFO 15484 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=skipDemoJob]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 65ms
2023-04-06 19:41:19.621 INFO 15484 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2023-04-06 19:41:19.625 INFO 15484 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
稍微解释一下:
核心处是
stepBuilderFactory.get("retryDemoStep")<String,String>chunk(5).reader(reader()).processor(retryItemProcessor).writer(retryItemWriter).faultTolerant().skip(CustomRetryException.class).skipLimit(5).build();
先把默认修改成可以容忍抛出异常,再设定会对特定的异常进行skip,最后设置skip的次数
5.4 错误跳过监听器(Skip Listener)
其他都与5.3中的一样,只是当我们需要监听skip时,需要用到
SkipListener<T, S>
MySkipListener.java
import org.springframework.batch.core.SkipListener;
import org.springframework.stereotype.Component;
@Component
public class MySkipListener implements SkipListener<String, String> {
@Override
public void onSkipInRead(Throwable throwable) {
}
@Override
public void onSkipInWrite(String s, Throwable throwable) {
}
@Override
public void onSkipInProcess(String item, Throwable throwable) {
System.out.println(item + "occurs exception: " + throwable);
}
}
SkipListenerDemo.java
import com.sb.error.retry.CustomRetryException;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableBatchProcessing
public class SkipListenerDemo {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
private ItemWriter<String> skipItemWriter;
@Autowired
private ItemProcessor<String, String> skipItemProcessor;
@Autowired
private MySkipListener mySkipListener;
@Bean
public Job skipListenerDemoJob() {
return jobBuilderFactory.get("skipListenerDemoJob")
.start(skipListenerDemoStep())
.build();
}
@Bean
public Step skipListenerDemoStep() {
return stepBuilderFactory.get("skipListenerDemoStep")
.<String, String>chunk(5)
.reader(reader())
.processor(skipItemProcessor)
.writer(skipItemWriter)
// (!)
.faultTolerant()
.skip(CustomRetryException.class)
.skipLimit(5)
// (!)
.listener(mySkipListener)
.build();
}
@Bean
@StepScope
public ListItemReader<String> reader() {
List<String> items = new ArrayList<>();
for (int i = 0; i < 30; i++) {
items.add("ITEM_" + String.valueOf(i));
}
ListItemReader<String> reader = new ListItemReader<>(items);
return reader;
}
}
console
2023-04-06 19:52:20.280 INFO 11568 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=skipListenerDemoJob]] launched with the following parameters: [{}]
2023-04-06 19:52:20.293 INFO 11568 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [skipListenerDemoStep]
processing item: ITEM_0
processing item: ITEM_1
processing item: ITEM_2
processing item: ITEM_3
processing item: ITEM_4
ITEM_0
ITEM_-1
ITEM_-2
ITEM_-3
ITEM_-4
processing item: ITEM_5
processing item: ITEM_6
processing item: ITEM_7
processing item: ITEM_8
processing item: ITEM_9
ITEM_-5
ITEM_-6
ITEM_-7
ITEM_-8
ITEM_-9
processing item: ITEM_10
processing item: ITEM_11
processing item: ITEM_12
processing item: ITEM_13
Process failure * 1.
processing item: ITEM_10
processing item: ITEM_11
processing item: ITEM_12
processing item: ITEM_14
ITEM_-10
ITEM_-11
ITEM_-12
ITEM_-14
ITEM_13occurs exception: com.sb.error.retry.CustomRetryException: Process failed and attempts: 1
processing item: ITEM_15
processing item: ITEM_16
processing item: ITEM_17
processing item: ITEM_18
processing item: ITEM_19
ITEM_-15
ITEM_-16
ITEM_-17
ITEM_-18
ITEM_-19
processing item: ITEM_20
processing item: ITEM_21
processing item: ITEM_22
processing item: ITEM_23
processing item: ITEM_24
ITEM_-20
ITEM_-21
ITEM_-22
ITEM_-23
ITEM_-24
processing item: ITEM_25
processing item: ITEM_26
processing item: ITEM_27
processing item: ITEM_28
processing item: ITEM_29
ITEM_-25
ITEM_-26
ITEM_-27
ITEM_-28
ITEM_-29
2023-04-06 19:52:20.349 INFO 11568 --- [ main] o.s.batch.core.step.AbstractStep : Step: [skipListenerDemoStep] executed in 56ms
2023-04-06 19:52:20.356 INFO 11568 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=skipListenerDemoJob]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 72ms
2023-04-06 19:52:20.360 INFO 11568 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2023-04-06 19:52:20.365 INFO 11568 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.