Penjadwalan di Musim Semi dengan Quartz

1. Ikhtisar

Dalam tutorial ini kita akan membuat Penjadwal sederhana di Musim Semi dengan Quartz .

Kami akan mulai dengan tujuan sederhana - untuk dengan mudah mengkonfigurasi pekerjaan terjadwal yang baru.

1.1. Komponen Utama dari Quartz API

Quartz memiliki arsitektur modular. Ini terdiri dari beberapa komponen dasar yang dapat digabungkan sesuai kebutuhan. Dalam tutorial ini, kita akan fokus pada hal-hal yang umum untuk setiap pekerjaan: Job , JobDetail , Trigger , dan Scheduler .

Meskipun kita akan menggunakan Spring untuk mengelola aplikasi, masing-masing komponen dapat dikonfigurasi dengan dua cara: cara Quartz atau cara Spring (menggunakan kelas praktisnya).

Kami akan membahas keduanya sejauh mungkin demi kelengkapan, tetapi keduanya dapat diadopsi. Mari mulai membangun, satu komponen dalam satu waktu.

2. Job dan JobDetail

2.1. Pekerjaan

API menyediakan antarmuka Pekerjaan yang hanya memiliki satu metode - eksekusi. Ini harus dilaksanakan oleh kelas yang berisi pekerjaan aktual yang harus dilakukan, yaitu tugas. Saat pemicu pekerjaan diaktifkan, penjadwal memanggil metode eksekusi , meneruskannya ke objek JobExecutionContext .

The JobExecutionContext memberikan contoh pekerjaan dengan informasi tentang lingkungan runtime, termasuk pegangan untuk scheduler, pegangan untuk memicu, dan pekerjaan ini JobDetail objek.

Dalam contoh singkat ini - tugas mendelegasikan tugas ke kelas layanan:

@Component public class SampleJob implements Job { @Autowired private SampleJobService jobService; public void execute(JobExecutionContext context) throws JobExecutionException { jobService.executeSampleJob(); } } 

2.2. JobDetail

Meskipun pekerjaannya adalah pekerja keras, Quartz tidak menyimpan instance sebenarnya dari kelas pekerjaan tersebut. Sebagai gantinya, kita dapat mendefinisikan sebuah instance dari Job menggunakan kelas JobDetail . Kelas pekerjaan harus diberikan ke JobDetail sehingga mengetahui jenis pekerjaan yang akan dijalankan.

2.3. Kuarsa JobBuilder

Quartz JobBuilder menyediakan API bergaya builder untuk membuat entitas JobDetail .

@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

JobDetailFactoryBean Spring menyediakan penggunaan gaya kacang untuk mengonfigurasi instance JobDetail . Ini menggunakan nama kacang musim semi sebagai nama pekerjaan, jika tidak ditentukan lain:

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

Instance baru JobDetail dibuat untuk setiap eksekusi pekerjaan. The JobDetail objek yang melekat sifat rinci dari pekerjaan. Setelah eksekusi selesai, referensi ke instance tersebut akan dihapus.

3. Pemicu

Sebuah Pemicu adalah mekanisme untuk jadwal Job , yaitu Pemicu misalnya “kebakaran” pelaksanaan pekerjaan. Ada pemisahan tanggung jawab yang jelas antara Job (gagasan tugas) dan Trigger (mekanisme penjadwalan).

Selain Job , trigger juga membutuhkan tipe yang bisa dipilih berdasarkan persyaratan penjadwalan.

Katakanlah, kita ingin menjadwalkan tugas kita untuk dijalankan setiap satu jam sekali, tanpa batas - kita dapat menggunakan TriggerBuilder Quartz atau SimpleTriggerFactoryBean Spring untuk melakukannya.

3.1. Quartz TriggerBuilder

TriggerBuilder adalah API bergaya builder untuk membuat entitas Pemicu :

@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

SimpleTriggerFactoryBean menyediakan penggunaan gaya kacang untuk mengonfigurasi SimpleTrigger . Ini menggunakan nama kacang musim semi sebagai nama pemicu dan default untuk pengulangan tak terbatas, jika tidak ditentukan lain:

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

4. Mengonfigurasi JobStore

JobStore menyediakan mekanisme penyimpanan untuk Pekerjaan dan Pemicu , dan bertanggung jawab untuk memelihara semua data yang relevan dengan penjadwal pekerjaan. API mendukung penyimpanan dalam memori dan persisten .

4.1. JobStore Dalam Memori

Sebagai contoh, kita akan menggunakan RAMJobStore dalam memori yang menawarkan kinerja sangat cepat dan konfigurasi sederhana melalui quartz.properties :

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

Kelemahan yang jelas dari RAMJobStore adalah sifatnya yang mudah berubah . Semua informasi penjadwalan hilang antara shutdown. Jika definisi pekerjaan dan jadwal harus disimpan di antara shutdown, JDBCJobStore yang persisten harus digunakan sebagai gantinya.

Untuk mengaktifkan JobStore dalam memori di Spring , kami menyetel properti ini di application.properties kami :

spring.quartz.job-store-type=memory

4.2. JDBC JobStore

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

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.

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:

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. Firstly, we set the store type in our application.properties:

spring.quartz.job-store-type=jdbc

Next, we 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:

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

5. Scheduler

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

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

5.1. Quartz 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:

@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

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

@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

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.

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 like so:

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

6. Conclusion

That's all. We have just built our first basic scheduler using the Quartz API as well as Spring's convenience classes.

The key takeaway from this tutorial is that we were able to configure a job with just a few lines of code and without using any XML-based configuration.

The complete source code for the example is available in this github project. It is a Maven project which can be imported and run as-is. The default setting uses Spring's convenience classes, which can be easily switched to Quartz API with a run-time parameter (refer to the README.md in the repository).