我在春季批次中发送电子邮件Tasklet
.
SMTP 服务器已关闭,因此未选中MailSendException
发生异常。
过渡的下一步声明为(来自电子邮件发送):
FlowBuilder<Flow> flowBuilder = new FlowBuilder<Flow>("myFlow")
.from(sendNotificationStep()).next(nextStep());
即使在未经检查的异常情况下也会执行nextStep()
。
这是 Spring 批处理框架忽略未经检查的异常的正常行为吗?
问题是此异常被静默忽略并且未记录(我将root
记录器设置为WARN
)。
为什么事务在运行时异常上回滚而不是 SQLException 中报告的行为有些相反
更新使用调试器单步执行后,我结束于内部:
public class SimpleFlow implements Flow, InitializingBean {
public FlowExecution resume(String stateName, FlowExecutor executor) throws FlowExecutionException {
state = nextState(stateName, status, stepExecution);
status
是FAILED
,state
是sendNotificationStep
的,nextState()
返回nextStep
。
resume
有catch
:
catch (Exception e) {
executor.close(new FlowExecution(stateName, status));
throw new FlowExecutionException(String.format("Ended flow=%s at state=%s with exception", name,
stateName), e);
}
但异常之前由以下人员处理:
public abstract class AbstractStep implements Step, InitializingBean, BeanNameAware {
public final void execute(StepExecution stepExecution) throws JobInterruptedException,
catch (Throwable e) {
stepExecution.upgradeStatus(determineBatchStatus(e));
exitStatus = exitStatus.and(getDefaultExitStatusForFailure(e));
stepExecution.addFailureException(e);
if (stepExecution.getStatus() == BatchStatus.STOPPED) {
logger.info(String.format("Encountered interruption executing step %s in job %s : %s", name, stepExecution.getJobExecution().getJobInstance().getJobName(), e.getMessage()));
if (logger.isDebugEnabled()) {
logger.debug("Full exception", e);
}
}
else {
logger.error(String.format("Encountered an error executing step %s in job %s", name, stepExecution.getJobExecution().getJobInstance().getJobName()), e);
}
}
批处理管理员将问题步骤列为ABANDONED
。
更新 3重现行为的全功能示例(感谢Sabir Khan提供刺伤!
@SpringBootApplication
@Configuration
@EnableBatchProcessing
public class X {
private static final Logger logger = LoggerFactory.getLogger(X.class);
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
@Bean
protected Tasklet tasklet1() {
return (StepContribution contribution, ChunkContext context) -> {
logger.warn("Inside tasklet1");
throw new IllegalStateException("xxx");
//return RepeatStatus.FINISHED;
};
}
@Bean
protected Tasklet tasklet2() {
return (StepContribution contribution, ChunkContext context) -> {
logger.warn("Inside tasklet2");
return RepeatStatus.FINISHED;
};
}
@Bean
public Job job() throws Exception {
Flow flow = new FlowBuilder<Flow>("myFlow").from(firstStep()).on("*").to(nextStep()).end();
return this.jobs.get("job").start(flow).end().build();
}
@Bean
protected Step firstStep() {
return this.steps.get("firstStep").tasklet(tasklet1()).build();
}
@Bean
protected Step nextStep() {
return this.steps.get("nextStep").tasklet(tasklet2()).build();
}
public static void main(String[] args) throws Exception {
System.exit(SpringApplication.exit(SpringApplication.run(X.class, args)));
}
}
不,这不是正常的春季批处理行为,我从未见过您所描述的内容。
我认为,Spring Batch 不会区分被选中或未选中的异常 - 当为顺序执行的情况抛出异常时,流将在那个点停止。
显然,如果并行步骤或执行正在进行,其他部分的执行将继续。
在某个地方,异常可能已经在您的代码中处理(静默地吃掉),这就是为什么执行可能会继续下一步的原因。
我已经看到在我的作业中抛出这两类异常,并且作业的行为方式与我编码跳过重试和其他异常处理机制的方式相同,它与 Spring Batch 无关 - 这是纯 Java。
我不确定我是否误读了您的问题,但下面的示例代码无法按照您描述的方式工作。Step-1 抛出未经检查的异常并且执行就在那里停止,因为我无论如何都没有处理它。
@SpringBootApplication(exclude = { DataSource.class,
DataSourceAutoConfiguration.class })
@Configuration
@EnableBatchProcessing
public class AppConfiguration {
private static final Logger logger = LoggerFactory.getLogger(AppConfiguration.class);
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
@Autowired
private JavaMailSender javaMailSender;
@Bean
protected Tasklet tasklet() {
return new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext context) {
MimeMessage message = javaMailSender.createMimeMessage();
javaMailSender.send(message);
return RepeatStatus.FINISHED;
}
};
}
@Bean
protected Tasklet tasklet2() {
return new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext context) {
return RepeatStatus.FINISHED;
}
};
}
@Bean
public Job job() throws Exception {
Flow flow = new FlowBuilder<Flow>("myFlow").from(step1()).next(nextStep()).end();
return this.jobs.get("job").start(flow).end().build();
}
@Bean
protected Step step1() {
return this.steps.get("step1").tasklet(tasklet()).build();
}
@Bean
protected Step nextStep() {
return this.steps.get("nextStep").tasklet(tasklet2()).build();
}
public static void main(String[] args) throws Exception {
System.exit(SpringApplication.exit(SpringApplication.run(AppConfiguration.class, args)));
}
}
希望有帮助!!