Scheduling in Spring with Quartz – 用Quartz在Spring进行调度

最后修改: 2015年 10月 8日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll build a simple Scheduler in Spring with Quartz.

在本教程中,我们将用Quartz构建一个简单的Scheduler in Spring

We’ll begin with a simple goal in mind, to easily configure a new scheduled job.

我们将从一个简单的目标开始,即轻松地配置一个新的预定作业。

1.1. Key Components of the Quartz API

1.1.Quartz API的关键组成部分

Quartz has a modular architecture. It consists of several basic components that we can combine as required. In this tutorial, we’ll focus on the ones that are common to every job: Job, JobDetail, Trigger and Scheduler.

Quartz有一个模块化的架构。它由几个基本组件组成,我们可以根据需要进行组合。在本教程中,我们将重点讨论每个工作所共有的组件。Job,JobDetail,Trigger Scheduler

Although we’ll use Spring to manage the application, each individual component can be configured in two ways: the Quartz way or the Spring way (using its convenience classes).

尽管我们将使用Spring来管理应用程序,但每个单独的组件可以通过两种方式进行配置:Quartz方式或Spring方式(使用其便利类)。

We’ll cover both options as far as possible, for the sake of completeness, but we may adopt either. Now let’s start building, one component at a time.

为了完整起见,我们将尽可能地涵盖这两种选择,但我们可以采用其中任何一种。现在让我们开始构建,一次一个组件。

2. Job and JobDetail

2.JobJobDetail

2.1. Job

2.1.工作

The API provides a Job interface that has just one method, execute. It must be implemented by the class that contains the actual work to be done, i.e. the task. When a job’s trigger fires, the scheduler invokes the execute method, passing it a JobExecutionContext object.

API提供了一个Job接口,它只有一个方法,execute.它必须由包含实际工作的类来实现,即任务。当一个工作的触发器启动时,调度器会调用execute方法,并传递给它一个JobExecutionContext对象。

The JobExecutionContext provides the job instance with information about its runtime environment, including a handle to the scheduler, a handle to the trigger, and the job’s JobDetail object.

JobExecutionContext为作业实例提供了关于其运行环境的信息,包括调度器的句柄、触发器的句柄以及作业的JobDetail对象。

In this quick example, the job delegates the task to a service class:

在这个快速的例子中,工作将任务委托给一个服务类。

@Component
public class SampleJob implements Job {

    @Autowired
    private SampleJobService jobService;

    public void execute(JobExecutionContext context) throws JobExecutionException {
        jobService.executeSampleJob();
    }
}

2.2. JobDetail

2.2.JobDetail

While the job is the workhorse, Quartz doesn’t store an actual instance of the job class. Instead, we can define an instance of the Job using the JobDetail class. The job’s class must be provided to the JobDetail, so that it knows the type of the job to be executed.

虽然作业是工作的主体,但Quartz并不存储作业类的实际实例。相反,我们可以使用JobDetail类来定义Job的实例。工作的类必须提供给JobDetail,,这样它就知道要执行的工作的类型

2.3. Quartz JobBuilder

2.3.石英JobBuilder

The Quartz JobBuilder provides a builder-style API for constructing JobDetail entities:

Quartz的JobBuilder提供了一个构建JobDetail实体的构建器式API。

@Bean
public JobDetail jobDetail() {
    return JobBuilder.newJob().ofType(SampleJob.class)
      .storeDurably()
      .withIdentity("Qrtz_Job_Detail")  
      .withDescription("Invoke Sample Job service...")
      .build();
}

2.4. Spring JobDetailFactoryBean

2.4.Spring JobDetailFactoryBean

Spring’s JobDetailFactoryBean provides bean-style usage for configuring JobDetail instances. It uses the Spring bean name as the job name, if not otherwise specified:

Spring的JobDetailFactoryBean为配置JobDetail实例提供了Bean式用法。如果没有另外指定,它使用Spring Bean的名称作为工作名称。

@Bean
public JobDetailFactoryBean jobDetail() {
    JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean();
    jobDetailFactory.setJobClass(SampleJob.class);
    jobDetailFactory.setDescription("Invoke Sample Job service...");
    jobDetailFactory.setDurability(true);
    return jobDetailFactory;
}

Every execution of the job creates a new instance of JobDetail. The JobDetail object conveys the detailed properties of the job. Once the execution is complete, references to the instance are dropped.

工作的每一次执行都会创建一个新的JobDetail的实例。JobDetail对象传达作业的详细属性。一旦执行完成,对该实例的引用就会被删除。

3. Trigger

3.触发器

A Trigger is the mechanism to schedule a Job, i.e. a Trigger instance “fires” the execution of a job. There’s a clear separation of responsibilities between the Job (notion of task) and Trigger (scheduling mechanism).

Trigger是安排Job的机制,即Trigger实例 “启动 “作业的执行。在Job(任务的概念)和Trigger(调度机制)之间有明确的职责分工。

In addition to a Job, the trigger also needs a type, which we can choose based on the scheduling requirements.

除了一个Job,触发器还需要一个type,我们可以根据调度要求来选择。

Let’s say we want to schedule our task to execute once every hour indefinitely, then we can use Quartz’s TriggerBuilder or Spring’s SimpleTriggerFactoryBean to do so.

比方说,我们想把我们的任务安排成每小时执行一次不确定,那么我们可以使用Quartz的TriggerBuilder或Spring的SimpleTriggerFactoryBean来实现。

3.1. Quartz TriggerBuilder

3.1.石英TriggerBuilder

TriggerBuilder is a builder-style API for constructing the Trigger entity:

TriggerBuilder是一个构建Trigger实体的构建器式API。

@Bean
public Trigger trigger(JobDetail job) {
    return TriggerBuilder.newTrigger().forJob(job)
      .withIdentity("Qrtz_Trigger")
      .withDescription("Sample trigger")
      .withSchedule(simpleSchedule().repeatForever().withIntervalInHours(1))
      .build();
}

3.2. Spring SimpleTriggerFactoryBean

3.2.Spring SimpleTriggerFactoryBean

SimpleTriggerFactoryBean provides bean-style usage for configuring SimpleTrigger. It uses the Spring bean name as the trigger name, and defaults to indefinite repetition if not otherwise specified:

SimpleTriggerFactoryBean为配置SimpleTrigger提供了Bean式用法。它使用Spring Bean的名称作为触发器的名称,如果没有另行指定,则默认为无限期重复。

@Bean
public SimpleTriggerFactoryBean trigger(JobDetail job) {
    SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean();
    trigger.setJobDetail(job);
    trigger.setRepeatInterval(3600000);
    trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
    return trigger;
}

4. Configuring the JobStore

4.配置JobStore

JobStore provides the storage mechanism for the Job and Trigger. It’s also responsible for maintaining all the data relevant to the job scheduler. The API supports both in-memory and persistent stores.

JobStoreJobTrigger提供存储机制。它还负责维护与作业调度器相关的所有数据。该API同时支持内存中持久化存储。

4.1. In-Memory JobStore

4.1.内存中的JobStore

For our example, we’ll use the in-memory RAMJobStore, which offers blazing-fast performance and simple configuration via quartz.properties:

在我们的例子中,我们将使用内存中的RAMJobStore,,它提供了极快的性能和通过quartz.properties的简单配置。

org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore

The obvious drawback of the RAMJobStore is that it’s volatile in nature. All the scheduling information is lost between shutdowns. If we need to keep job definitions and schedules between shutdowns, we can use the persistent JDBCJobStore instead.

RAMJobStore的明显缺点是它具有易失性的性质。所有的调度信息在关机之间都会丢失。如果我们需要在关闭期间保留作业定义和计划,我们可以使用持久化的JDBCJobStore来代替。

To enable an in-memory JobStore in Spring, we’ll set this property in our application.properties:

为了在Spring中启用内存中的JobStore,我们将在application.properties中设置这个属性。

spring.quartz.job-store-type=memory

4.2. JDBC JobStore

4.2 JDBCJobStore

There are two types of JDBCJobStore: JobStoreTX and JobStoreCMT. They both do the same job of storing scheduling information in a database.

有两种类型的JDBCJobStoreJobStoreTXJobStoreCMT。它们都做同样的工作,即在数据库中存储调度信息。

The difference between the two is how they manage the transactions that commit the data. The JobStoreCMT type requires an application transaction to store data, whereas the JobStoreTX type starts and manages its own transactions.

两者之间的区别在于它们如何管理提交数据的事务。JobStoreCMT类型需要一个应用程序事务来存储数据,而JobStoreTX类型则启动并管理自己的事务。

There are several properties to set for a JDBCJobStore. At a minimum, we must specify the type of JDBCJobStore, the data source, and the database driver class. There are driver classes for most databases, but StdJDBCDelegate covers most cases:

有几个属性需要为JDBCJobStore设置。至少,我们必须指定JDBCJobStore的类型、数据源和数据库驱动类。大多数数据库都有驱动类,但StdJDBCDelegate涵盖了大多数情况。

org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource=quartzDataSource

Setting up a JDBC JobStore in Spring takes a few steps. First, we’ll set the store type in our application.properties:

在Spring中设置一个JDBC JobStore需要几个步骤。首先,我们要在application.properties中设置存储类型。

spring.quartz.job-store-type=jdbc

Then we’ll need to enable auto-configuration and give Spring the data source needed by the Quartz scheduler. The @QuartzDataSource annotation does the hard work in configuring and initializing the Quartz database for us:

然后我们需要启用自动配置,给Spring提供Quartz调度器所需的数据源。@QuartzDataSource注解为我们完成了配置和初始化Quartz数据库的艰苦工作。

@Configuration
@EnableAutoConfiguration
public class SpringQrtzScheduler {

    @Bean
    @QuartzDataSource
    public DataSource quartzDataSource() {
        return DataSourceBuilder.create().build();
    }
}

5. Scheduler

5.计划表

The Scheduler interface is the main API for interfacing with the job scheduler.

Scheduler 接口是与作业调度器对接的主要API。

A Scheduler can be instantiated with a SchedulerFactory. Once created, we can register Jobs and Triggers with it. Initially, the Scheduler is in “stand-by” mode, and we must invoke its start method to start the threads that fire the execution of jobs.

Scheduler可以通过SchedulerFactory来实例化。一旦创建,我们就可以用它来注册Jobs和Triggers。最初,Scheduler处于 “待机 “模式,我们必须调用它的start方法来启动执行作业的线程。

5.1. Quartz StdSchedulerFactory

5.1.石英StdSchedulerFactory

By simply invoking the getScheduler method on the StdSchedulerFactory, we can instantiate the Scheduler, initialize it (with the configured JobStore and ThreadPool), and return a handle to its API:

通过简单地调用StdSchedulerFactory上的getScheduler方法,我们可以实例化Scheduler,初始化它(使用配置的JobStoreThreadPool),并返回一个通向其API的处理。

@Bean
public Scheduler scheduler(Trigger trigger, JobDetail job, SchedulerFactoryBean factory) 
  throws SchedulerException {
    Scheduler scheduler = factory.getScheduler();
    scheduler.scheduleJob(job, trigger);
    scheduler.start();
    return scheduler;
}

5.2. Spring SchedulerFactoryBean

5.2.Spring SchedulerFactoryBean

Spring’s SchedulerFactoryBean provides bean-style usage for configuring a Scheduler, managing its life-cycle within the application context, and exposing the Scheduler as a bean for dependency injection:

Spring的 SchedulerFactoryBean为配置Scheduler提供了Bean式的用法,在应用程序上下文中管理其生命周期,并将Scheduler作为Bean进行依赖注入。

@Bean
public SchedulerFactoryBean scheduler(Trigger trigger, JobDetail job, DataSource quartzDataSource) {
    SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
    schedulerFactory.setConfigLocation(new ClassPathResource("quartz.properties"));

    schedulerFactory.setJobFactory(springBeanJobFactory());
    schedulerFactory.setJobDetails(job);
    schedulerFactory.setTriggers(trigger);
    schedulerFactory.setDataSource(quartzDataSource);
    return schedulerFactory;
}

5.3. Configuring SpringBeanJobFactory

5.3.配置SpringBeanJobFactory

The SpringBeanJobFactory provides support for injecting the scheduler context, job data map, and trigger data entries as properties into the job bean while creating an instance.

SpringBeanJobFactory提供了支持,在创建实例时将调度器上下文、作业数据映射和触发器数据条目作为属性注入作业Bean中。

However, it lacks support for injecting bean references from the application context. Thanks to the author of this blog post, we can add auto-wiring support to SpringBeanJobFactory: 

然而,它缺乏对从应用上下文注入Bean引用的支持。感谢这篇博文的作者,我们可以在SpringBeanJobFactory中添加自动布线支持。

@Bean
public SpringBeanJobFactory springBeanJobFactory() {
    AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
    jobFactory.setApplicationContext(applicationContext);
    return jobFactory;
}

6. Conclusion

6.结论

In this article, we built our first basic scheduler using the Quartz API, as well as Spring’s convenience classes.

在这篇文章中,我们使用Quartz API以及Spring的便利类建立了我们的第一个基本调度器。

The key takeaway is that we’re able to configure a job with just a few lines of code, without using any XML-based configuration.

关键的收获是,我们能够用几行代码来配置一个作业,而不使用任何基于XML的配置。

The complete source code for the example is available in this github project. This is a Maven project, so we can import it and run it as-is. The default setting uses Spring’s convenience classes, but we can easily switch it to Quartz API with a run-time parameter (refer to the README.md in the repository).

该示例的完整源代码可在这个github项目中找到。这是一个Maven项目,所以我们可以导入它并按原样运行。默认设置使用Spring的便利类,但我们可以通过运行时参数轻松将其切换为Quartz API(参考资源库中的README.md)。