在每个 Spring 计划 (@Scheduled) 运行 之前重置状态
Reset state before each Spring scheduled (@Scheduled) run
我有一个 Spring 启动批处理应用程序需要每天 运行。它每天读取一个文件,对其数据进行一些处理,并将处理后的数据写入数据库。在此过程中,应用程序会保存一些状态,例如要读取的文件(存储在 FlatFileItemReader
和 JobParameters
中)、运行 的当前日期和时间、一些文件数据阅读项目之间的比较等
一个调度选项是使用 Spring 的 @Scheduled
,例如:
@Scheduled(cron = "${schedule}")
public void runJob() throws Exception {
jobRunner.runJob(); //runs the batch job by calling jobLauncher.run(job, jobParameters);
}
这里的问题是状态保持在 运行s 之间。所以,我不得不更新要读取的文件,运行的当前日期和时间,清除缓存的文件数据等
另一种选择是 运行 通过 unix cron 作业的应用程序。这显然会满足在 运行 之间清除状态的需要,但我更喜欢将作业调度与应用程序而不是 OS 联系起来(并且更喜欢它而不是 OS 不可知论者)。可以在 @Scheduled
运行 秒之间重置应用程序状态吗?
您始终可以将执行您的任务(更重要的是,保持您的状态)的代码移动到原型范围的 bean 中。然后,每次您的计划方法为 运行.
时,您都可以从应用程序上下文中检索该 bean 的新实例
例子
我创建了一个 GitHub 存储库,其中包含我正在谈论的工作示例,但它的要点在于这两个 类:
ScheduledTask.java
注意 @Scope
注释。它指定此组件不应是单例。 randomNumber
字段表示我们希望在每次调用时重置的状态。 "Reset"在这种情况下意味着生成了一个新的随机数,只是为了表明它确实发生了变化。
@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
Thomas 的方法似乎是一个合理的解决方案,这就是我赞成它的原因。缺少的是如何在 spring 批处理作业的情况下应用它。因此我稍微调整了他的例子:
@Component
public class JobCreatorComponent {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Job createJob() {
// use the jobBuilderFactory to create your job as usual
return jobBuilderFactory.get() ...
}
}
你的组件有 launch 方法
@零件
public class 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);
}
我还没有尝试过代码,但这是我会尝试的方法。
构建作业时,重要的是要确保此作业中使用的所有 reader、处理器和编写器也是全新的实例。这意味着,如果它们没有被实例化为纯 java 对象(不是 spring bean)或具有范围 "step" 的 spring bean,你必须确保始终有一个新实例使用过。
已编辑:
如何处理 SingeltonBeans
有时单例 bean 无法避免,在这些情况下必须有办法 "reset" 它们。
一种简单的方法是定义一个接口 "ResetableBean",其中包含由此类 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());
...
我有一个 Spring 启动批处理应用程序需要每天 运行。它每天读取一个文件,对其数据进行一些处理,并将处理后的数据写入数据库。在此过程中,应用程序会保存一些状态,例如要读取的文件(存储在 FlatFileItemReader
和 JobParameters
中)、运行 的当前日期和时间、一些文件数据阅读项目之间的比较等
一个调度选项是使用 Spring 的 @Scheduled
,例如:
@Scheduled(cron = "${schedule}")
public void runJob() throws Exception {
jobRunner.runJob(); //runs the batch job by calling jobLauncher.run(job, jobParameters);
}
这里的问题是状态保持在 运行s 之间。所以,我不得不更新要读取的文件,运行的当前日期和时间,清除缓存的文件数据等
另一种选择是 运行 通过 unix cron 作业的应用程序。这显然会满足在 运行 之间清除状态的需要,但我更喜欢将作业调度与应用程序而不是 OS 联系起来(并且更喜欢它而不是 OS 不可知论者)。可以在 @Scheduled
运行 秒之间重置应用程序状态吗?
您始终可以将执行您的任务(更重要的是,保持您的状态)的代码移动到原型范围的 bean 中。然后,每次您的计划方法为 运行.
时,您都可以从应用程序上下文中检索该 bean 的新实例例子
我创建了一个 GitHub 存储库,其中包含我正在谈论的工作示例,但它的要点在于这两个 类:
ScheduledTask.java
注意 @Scope
注释。它指定此组件不应是单例。 randomNumber
字段表示我们希望在每次调用时重置的状态。 "Reset"在这种情况下意味着生成了一个新的随机数,只是为了表明它确实发生了变化。
@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
Thomas 的方法似乎是一个合理的解决方案,这就是我赞成它的原因。缺少的是如何在 spring 批处理作业的情况下应用它。因此我稍微调整了他的例子:
@Component
public class JobCreatorComponent {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Job createJob() {
// use the jobBuilderFactory to create your job as usual
return jobBuilderFactory.get() ...
}
}
你的组件有 launch 方法 @零件 public class 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);
}
我还没有尝试过代码,但这是我会尝试的方法。
构建作业时,重要的是要确保此作业中使用的所有 reader、处理器和编写器也是全新的实例。这意味着,如果它们没有被实例化为纯 java 对象(不是 spring bean)或具有范围 "step" 的 spring bean,你必须确保始终有一个新实例使用过。
已编辑: 如何处理 SingeltonBeans 有时单例 bean 无法避免,在这些情况下必须有办法 "reset" 它们。
一种简单的方法是定义一个接口 "ResetableBean",其中包含由此类 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());
...