在每次Spring计划(@Scheduled)运行之前重置状态

时间:2016-08-25 16:57:24

标签: spring spring-boot spring-batch schedule spring-scheduled

我有一个需要每天运行的Spring Boot Batch应用程序。它读取每日文件,对其数据进行一些处理,并将处理后的数据写入数据库。在此过程中,应用程序保存一些状态,例如要读取的文件(存储在FlatFileItemReaderJobParameters中),当前运行的日期和时间,一些文件数据用于读取项目之间的比较等等。

安排的一个选择是使用Spring的@Scheduled,例如:

@Scheduled(cron = "${schedule}")
public void runJob() throws Exception {
    jobRunner.runJob(); //runs the batch job by calling jobLauncher.run(job, jobParameters);
}

这里的问题是在运行之间维持状态。因此,我必须更新要读取的文件,运行的当前日期和时间,清除缓存的文件数据等。

另一种选择是通过unix cron作业运行应用程序。这显然满足了在运行之间清除状态的需要,但我更喜欢将作业调度绑定到应用程序而不是OS(并且更喜欢它与操作系统无关)。可以在@Scheduled次运行之间重置应用程序状态吗?

2 个答案:

答案 0 :(得分:4)

您总是可以将执行任务的代码(更重要的是,保持您的状态)移动到原型范围的bean中。然后,每次运行计划的方法时,您都可以从应用程序上下文中检索该bean的新实例。

实施例

我创建了一个GitHub存储库,其中包含我正在谈论的一个工作示例,但它的要点在于这两个类:

<强> ScheduledTask.java

注意@Scope注释。它指定此组件不应是单个组件。 randomNumber字段表示我们希望在每次调用时重置的状态。在这种情况下,“重置”意味着生成一个新的随机数,只是为了表明它确实发生了变化。

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
class ScheduledTask {

    private double randomNumber = Math.random();

    void execute() {
        System.out.printf(
            "Executing task from %s. Random number is %f%n",
            this,
            randomNumber
        );
    }
}

<强> TaskScheduler.java

通过ApplicationContext中的自动装配,您可以在scheduledTask方法中使用它来检索ScheduledTask的新实例。

@Component
public class TaskScheduler {

    @Autowired
    private ApplicationContext applicationContext;

    @Scheduled(cron = "0/5 * * * * *")
    public void scheduleTask() {
        ScheduledTask task = applicationContext.getBean(ScheduledTask.class);
        task.execute();
    }
}

<强>输出

运行代码时,这是一个示例:

Executing task from com.thomaskasene.example.schedule.reset.ScheduledTask@329c8d3d. Random number is 0.007027
Executing task from com.thomaskasene.example.schedule.reset.ScheduledTask@3c5b751e. Random number is 0.145520
Executing task from com.thomaskasene.example.schedule.reset.ScheduledTask@3864e64d. Random number is 0.268644

答案 1 :(得分:1)

托马斯&#39;方法似乎是一个合理的解决方案,这就是我投票的原因。缺少的是如何在弹簧批处理作业中应用它。因此我稍微调整了他的例子:

@Component
public class JobCreatorComponent {


    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Job createJob() {
       // use the jobBuilderFactory to create your job as usual
       return jobBuilderFactory.get() ...
    }
}
使用启动方法

您的组件     @零件     公共类ScheduledLauncher {

   @Autowired
   private ... jobRunner;

   @Autwired
   private JobCreatorComponent creator;

@Scheduled(cron = "${schedule}")
public void runJob() throws Exception {

    // it would probably make sense to check the applicationContext and
    // remove any existing job

    creator.createJob(); // this should create a complete new instance of 
                         //  the Job
    jobRunner.runJob(); //runs the batch job by calling jobLauncher.run(job, jobParameters);
}

我还没有尝试过代码,但这是我会尝试的方法。

构建作业时,务必确保此作业中使用的所有读取器,处理器和编写器都是完整的新实例。这意味着,如果它们没有被实例化为纯Java对象(不是作为spring bean)或者是具有范围&#34;步骤&#34;您必须确保始终使用新实例。

<强>编辑: 如何处理SingeltonBeans 有时单身豆无法预防,在这些情况下必须有一种方法来重置&#34;它们。

一种简单的方法是定义一个接口&#34; ResetableBean&#34;使用由此类bean实现的重置方法。然后,可以使用自动装配来收集所有此类bean的列表。

@Component
public class ScheduledLauncher {

    @Autowired
    private List<ResetableBean> resetables;

    ...

    @Scheduled(cron = "${schedule}")
    public void runJob() throws Exception {
       // reset all the singletons
       resetables.forEach(bean -> bean.reset());
       ...