Spring-batch:如何在 Spring Batch 中使用 skip 方法捕获异常消息?

Posted

技术标签:

【中文标题】Spring-batch:如何在 Spring Batch 中使用 skip 方法捕获异常消息?【英文标题】:Spring-batch : How to catch exception message with skip method in spring batch? 【发布时间】:2019-01-29 14:06:12 【问题描述】:

我是春季批次的新手。 我的问题是如何在 spring-batch 中使用 skip 方法捕获异常? 据我所知,当春季批处理中发生一些异常时,我们可以使用跳过方法来跳过它们。 但是如何使用跳过方法获取异常消息? 有人建议我使用 SkipListener ,这个类有三个像 onSkipInProcess() 这样的回调方法,但它对我没有用。 而且 ItemProcessListener 也不起作用。

如下代码:(我使用skip方法忽略异常,两个监听器接收异常信息)

Step mainStep = stepBuilder.get("run")
        .<ItemProcessing, ItemProcessing>chunk(5)
        .faultTolerant()
        .skip(IOException.class).skip(SocketTimeoutException.class)//skip IOException here
        .skipLimit(2000)
        .reader(reader)
        .processor(processor)
        .writer(writer)
        .listener(stepExecListener)
        .listener(new ItemProcessorListener()) //add process listener
        .listener(new SkipExceptionListener()) //add skip exception listner
        .build();

ItemProcessorListener 如下:

//(this class implements ItemProcessListener )

    @Override
    public void beforeProcess(Object item) 
        // TODO Auto-generated method stub

    
    @Override
    public void afterProcess(Object item, Object result) 
        logger.info("invoke remote finished, item=,result=",item,result);

    
    @Override
    public void onProcessError(Object item, Exception e) 
        logger.error("invoke remote error, item=,exception=,",item,e.getMessage(),e);
    

SkipExceptionListener 如下:

//(implements SkipListener<Object, Object>)


    @Override
    public void onSkipInRead(Throwable t) 
        // TODO Auto-generated method stub      
    

    @Override
    public void onSkipInWrite(Object item, Throwable t) 
        // TODO Auto-generated method stub      
    

    @Override
    public void onSkipInProcess(Object item, Throwable t) 
        logger.info("invoke remote finished,item=,itemJsonStr=,errMsg=,e=",
                    item,
                    JSONObject.toJSONString(item),
                    t.getMessage(),
                    t);
    

问题是所有记录器都不起作用。其实skip方法效果很好,我可以在表batch_step_execution中得到skip count。我不确定这两个监听器是否被回调。谁能告诉我我该怎么办?或者还有什么别的吗?非常感谢。

【问题讨论】:

【参考方案1】:

spring批处理中如何用skip方法捕获异常信息?

您可以通过实现SkipListener 接口或扩展SkipListenerSupport 类来做到这一点。 SkipListener 接口中的所有方法都有一个 Throwable 参数,这是引发的异常并导致项目被跳过。您可以在此处获取异常消息。这是一个例子:

import java.util.Arrays;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.SkipListener;
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.launch.JobLauncher;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableBatchProcessing
public class MyJob 

    @Autowired
    private JobBuilderFactory jobs;

    @Autowired
    private StepBuilderFactory steps;

    @Bean
    public ItemReader<Integer> itemReader() 
        return new ListItemReader<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
    

    @Bean
    public ItemWriter<Integer> itemWriter() 
        return items -> 
            for (Integer item : items) 
                System.out.println("item = " + item);
            
        ;
    

    @Bean
    public ItemProcessor<Integer, Integer> itemProcessor() 
        return item -> 
            if (item.equals(7)) 
                throw new IllegalArgumentException("Sevens are not accepted!!");
            
            return item;
        ;
    

    @Bean
    public Step step() 
        return steps.get("step")
                .<Integer, Integer>chunk(5)
                .reader(itemReader())
                .processor(itemProcessor())
                .writer(itemWriter())
                .faultTolerant()
                .skip(IllegalArgumentException.class)
                .skipLimit(3)
                .listener(new MySkipListener())
                .build();
    

    @Bean
    public Job job() 
        return jobs.get("job")
                .start(step())
                .build();
    

    public static class MySkipListener implements SkipListener<Integer, Integer> 

        @Override
        public void onSkipInRead(Throwable t) 

        

        @Override
        public void onSkipInWrite(Integer item, Throwable t) 

        

        @Override
        public void onSkipInProcess(Integer item, Throwable t) 
            System.out.println("Item " + item + " was skipped due to: " + t.getMessage());
        
    

    public static void main(String[] args) throws Exception 
        ApplicationContext context = new AnnotationConfigApplicationContext(MyJob.class);
        JobLauncher jobLauncher = context.getBean(JobLauncher.class);
        Job job = context.getBean(Job.class);
        jobLauncher.run(job, new JobParameters());
    


在本例中,MySkipListener 实现 SkipListener 并在您尝试执行的操作时从异常中获取消息。该示例读取从 1 到 10 的数字并跳过数字 7。您可以运行 main 方法,应该会看到以下输出:

item = 1
item = 2
item = 3
item = 4
item = 5
item = 6
item = 8
item = 9
item = 10
Item 7 was skipped due to: Sevens are not accepted!!

我希望这会有所帮助。

【讨论】:

它必须在 MyJob 类中还是只是为了方便示例? 不,它不必在 MyJob 类中。为了方便起见,我在那里添加了它。该示例是自包含的,您可以运行 main 方法并查看其工作原理。 感谢您的回复!它看到我以前的代码和你没有什么不同。不知道为什么我这边不行。我明天在公司再试一次。 @MahmoudBenHassine 我认为当我在作业类中移动 Skip-Listener 时它应该可以正常工作。并像你一样将类定义为静态的。我以为静态类首先会被初始化。所以它会正常工作。但实际上它也不起作用。我认为不同之处在于您的 spring-batch 版本与我不同?我的版本是 3.0。 我使用的是 4.0.1 版,但它应该适用于 3.0 版。监听器类可以是非静态的,没关系。这是一个你可以玩的项目:github.com/benas/sandbox/tree/master/so51981640。它使用版本 3,侦听器类是非静态的,并在 MyJob 类之外定义。【参考方案2】:

我无法让它与实现 SkipListener 一起工作(很高兴知道为什么),但最后我采用了仅在文档中简要提到的注释方式。也有人在这里使用实现方法(question)遇到了类似的问题,而答案中的人使用此注释方法而不是实现接口。

示例 bean:

@Component
public class CustomSkippedListener 

    @OnSkipInRead
    public void onSkipInRead(Throwable throwable) 
    

    @OnSkipInWrite
    public void onSkipInWrite(FooWritingDTO fooWritingDTO, Throwable throwable) 
        LOGGER.info("balabla" + throwable.getMessage());
    

    @OnSkipInProcess
    public void onSkipInProcess(FooLoaderDTO fooLoaderDTO, Throwable throwable) 
        LOGGER.info("blabla"  + throwable.getMessage());
    

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomSkippedListener.class);



然后像您一样自动装配并包含在步骤链中。

【讨论】:

感谢您的回复。 它可以与注解 (@OnSkipInProcess) 一起使用。我也会知道为什么实现接口不能工作。这是一个错误吗? skip listener可以是基于注解的组件,也可以实现SkipListener接口。两者都应该工作正常。我在答案中添加了一个示例,显示了一个实现接口并按预期调用的侦听器。

以上是关于Spring-batch:如何在 Spring Batch 中使用 skip 方法捕获异常消息?的主要内容,如果未能解决你的问题,请参考以下文章

如何确保 SFTP 会话始终在 spring-batch 结束时关闭

Spring-batch学习总结—ItemReader普通文件,数据库,XML,多文件数据读取

spring-batch (ItemProcessor) 数据处理过程

Spring-Batch 没有将元数据持久化到数据库?

使用 spring-boot 连接到 spring-batch 和应用程序数据库

Spring-Batch学习总结——重要概念,环境搭建,名词解释,第一个项目及异常处理