График през пролетта с кварц

1. Общ преглед

В този урок ще изградим прост планировчик през пролетта с кварц .

Ще започнем с една проста цел - лесно да конфигурираме нова планирана работа.

1.1. Ключови компоненти на кварцовия API

Кварцът има модулна архитектура. Състои се от няколко основни компонента, които могат да се комбинират според изискванията. В този урок ще се съсредоточим върху тези, които са общи за всяка работа: Job , JobDetail , Trigger и Scheduler .

Въпреки че ще използваме Spring за управление на приложението, всеки отделен компонент може да бъде конфигуриран по два начина: кварцов или Spring начин (използвайки неговите класове за удобство).

Ние ще обхванем и двете, доколкото е възможно, за пълнота, но може да бъде прието. Нека започнем да изграждаме, един по един компонент.

2. Job and JobDetail

2.1. Работа

API предоставя Job интерфейс, имащ само един метод - изпълнение. Той трябва да бъде изпълнен от класа, който съдържа действителната работа, която трябва да се свърши, т.е. задачата. Когато задейства задействането на заданието, планировщикът извиква метода за изпълнение , предавайки му обект JobExecutionContext .

В JobExecutionContext осигурява място за работа с информация за своята среда за изпълнение, включително и дръжка за планировчика, манипулатор на спусъка, а самата работа е JobDetail обект.

В този бърз пример - заданието делегира задачата в сервизен клас:

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

2.2. JobDetail

Докато работата е работният кон, Quartz не съхранява действителен екземпляр на класа работа. Вместо това можем да дефинираме екземпляр на Job, като използваме JobDetail класа. Класът на заданието трябва да бъде предоставен на JobDetail, така че да знае типа на заданието, което трябва да бъде изпълнено.

2.3. Кварцов JobBuilder

Quartz JobBuilder предоставя API в стил на конструктор за конструиране на JobDetail обекти .

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

2.4. Пролет JobDetailFactoryBean

JobDetailFactoryBean на Spring предоставя използване на боб стил за конфигуриране на екземпляри JobDetail . Той използва името на боб Spring като име на работа, ако не е посочено друго:

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

За всяко изпълнение на заданието се създава нов екземпляр на JobDetail . Обектът JobDetail предава подробните свойства на заданието. След като изпълнението приключи, препратките към екземпляра отпадат.

3. Задействане

А Trigger е механизмът да планирате работа , т.е. Trigger инстанция "пожари" Изпълнението на работа. Има ясно разделение на отговорностите между Job (понятие за задача) и Trigger (механизъм за планиране).

В допълнение към Job , спусъка също се нуждае от тип, който може да бъде избран въз основа на изискванията за планиране.

Да кажем, че искаме да планираме изпълнението на нашата задача веднъж на всеки час, за неопределено време - можем да използваме кварцовия TriggerBuilder или SpringTriggerFactoryBean за това.

3.1. Quartz TriggerBuilder

TriggerBuilder е API в стил на конструктор за конструиране на Trigger обект:

@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 осигурява използване на боб стил за конфигуриране на SimpleTrigger . Той използва името на боб Spring като име на задействане и по подразбиране за неопределено повторение, ако не е посочено друго:

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

4. Конфигуриране на JobStore

JobStore осигурява механизма за съхранение на заданието и задействането и е отговорен за поддържането на всички данни, свързани с планиращото задание. API поддържа както в паметта, така и постоянни хранилища.

4.1. JobStore в паметта

Например за целите ще използваме RAMJobStore в паметта, която предлага невероятно бърза работа и проста конфигурация чрез quartz.properties :

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

Очевидният недостатък на RAMJobStore е, че той е летлив по природа. Цялата информация за планирането се губи между изключванията. Ако дефинициите на работни места и графиците трябва да се съхраняват между изключванията, вместо това трябва да се използва постоянният JDBCJobStore .

За да активираме JobStore в паметта през пролетта , ние задаваме това свойство в нашата application.properties :

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).