preface

In our daily development, many times, the scheduled task is not written to death, but written to the database, so as to achieve the dynamic configuration of the scheduled task, the following through a simple example, to achieve this function.

Create a New SpringBoot project and add dependencies

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency><! -- For the convenience of testing, <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.1</version> <exclusions> <artifactId> slf4J-API </artifactId> <groupId>org.slf4j</groupId> </exclusion> </exclusions> </dependency> <dependency><! -- The dependence must be added, <groupId>org. springFramework </groupId> <artifactId> Spring-context-support </artifactId> </dependency>Copy the code

Configuration file application.properties

# Server port number
server.port=7902
Whether to generate DDL statements
spring.jpa.generate-ddl=false  
Whether to print SQL statements
spring.jpa.show-sql=true  
# Automatic DDL generation, set to None because specific DDL is specified
spring.jpa.hibernate.ddl-auto=none  
# Use H2 database
spring.datasource.platform=h2  
# specify the location of the schema file to generate the database
spring.datasource.schema=classpath:schema.sql  
# specify where the script is inserted into the database statement
spring.datasource.data=classpath:data.sql  
# Configure log information
logging.level.root=INFO  
logging.level.org.hibernate=INFO  
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE  
logging.level.org.hibernate.type.descriptor.sql.BasicExtractor=TRACE  
logging.level.com.itmuch=DEBUG
Copy the code

The Entity class

package com.chhliu.springboot.quartz.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Config { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column private String cron; / * * * @return the id
	 */
	public Long getId() {
		returnid; }... The getter and setter methods are omitted here... }Copy the code

The task class

package com.chhliu.springboot.quartz.entity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.stereotype.Component; Public class ScheduleTask {private static final Logger} {EnableScheduling {private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleTask.class); public voidsayHello(){
		LOGGER.info("Hello world, i'm the king of the world!!!"); }}Copy the code

Quartz configuration class

Since SpringBoot pursues zero XML configuration, this will be done by configuring beans

package com.chhliu.springboot.quartz.entity; import org.quartz.Trigger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.CronTriggerFactoryBean; import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean; import org.springframework.scheduling.quartz.SchedulerFactoryBean; @configuration public class QuartzConfigration {/** * attention: * Details: configures the scheduled task */ @bean (name ="jobDetail") public MethodInvokingJobDetailFactoryBean detailFactoryBean (ScheduleTask task) {/ / ScheduleTask for need to perform tasks MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean(); /* * Concurrent execution * For example, the task is executed every 5s, but the current task has not been executedtrue, the next task will be executed iffalse*/ jobdetail.setConcurrent (*/ jobdetail.setConcurrent (false);
        
        jobDetail.setName("srd-chhliu"); // Set the task name jobdetail.setgroup ("srd"); / / set the grouping of tasks, these properties can be stored in the database, use at multitasking / * * * for entity class needs to perform the corresponding object/jobDetail setTargetObject (task); / sayHello to need to perform method * * * through the configuration and tell JobDetailFactoryBean we need to perform regular ScheduleTask sayHello method in the class of * / jobDetail setTargetMethod ("sayHello");
        returnjobDetail; } /** * Details: */ @bean (name ="jobTrigger")
    public CronTriggerFactoryBean cronJobTrigger(MethodInvokingJobDetailFactoryBean jobDetail) {
        CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();
        tigger.setJobDetail(jobDetail.getObject());
        tigger.setCronExpression("0, 30, 20 * *?"); // The initial cron expression tigger.setname ("srd-chhliu"); / / the name of the triggerreturntigger; } /** * attention: * Details: define the Quartz scheduling factory */ @bean (name ="scheduler") public SchedulerFactoryBean schedulerFactory(Trigger cronJobTrigger) { SchedulerFactoryBean bean = new SchedulerFactoryBean(); / / for quartz clusters, QuartzScheduler startup update has the Job of the existence of bean. SetOverwriteExistingJobs (true); Bean.setstartupdelay (1); // Register trigger bean.settriggers (cronJobTrigger);returnbean; }}Copy the code

Check the database regularly and update the task

package com.chhliu.springboot.quartz.entity;
 
import javax.annotation.Resource;
 
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
import com.chhliu.springboot.quartz.repository.ConfigRepository;
 
@Configuration
@EnableScheduling
@Component
public class ScheduleRefreshDatabase {
	@Autowired
	private ConfigRepository repository;
 
	@Resource(name = "jobDetail")
	private JobDetail jobDetail;
 
	@Resource(name = "jobTrigger")
	private CronTrigger cronTrigger;
 
	@Resource(name = "scheduler") private Scheduler scheduler; @scheduled (fixedRate = 5000) And deciding whether or not to set the timer task according to the results of the query public void scheduleUpdateCronTrigger () throws SchedulerException {CronTrigger trigger = (CronTrigger) scheduler.getTrigger(cronTrigger.getKey()); String currentCron = trigger.getCronExpression(); // The current Trigger uses String searchCron = repository. FindOne (1L).getcron (); System.out.println(currentCron); System.out.println(searchCron);if(currentcron.equals (searchCron)) {// if the currentCron expression is the same as the cron expression queried from the database, the task is not refreshed}else{/ / expression scheduling builder CronScheduleBuilder scheduleBuilder = CronScheduleBuilder. CronSchedule (searchCron); // Rebuild trigger trigger = (CronTrigger) scheduler.getTrigger(CronTrigger.getkey ()); trigger = trigger.getTriggerBuilder().withIdentity(cronTrigger.getKey()) .withSchedule(scheduleBuilder).build(); Scheduler.reschedulejob (crontrigger.getkey (), trigger); currentCron = searchCron; }}}Copy the code

The related script

1, data. SQL

insert into config(id,cron) values(1,'0 0 over 2 * * * *? '); Perform a scheduled task every 2 minutes
Copy the code

2, schema. SQL

drop table config if exists;
create table config(
	id bigint generated by default as identity,
	cron varchar(40),
	primary key(id)
);
Copy the code

Run the test

The test results are as follows :(Quartz default thread pool size is 10)

0, 30, 20 * *? 0 0/2 * * *, right? The 18:02:00 2017-03-08. 5328-025 the INFO [eduler_Worker - 1] C.C.S.Q uartz. Entity. ScheduleTask: Hello world, I'm the king of the world!!! The 2017-03-08 18:04:00. 5328-003 the INFO [eduler_Worker - 2] C.C.S.Q uartz. Entity. ScheduleTask: Hello world, I 'm the king of the world!!! The 2017-03-08 18:06:00. 5328-002 the INFO [eduler_Worker - 3] C.C.S.Q uartz. Entity. ScheduleTask: Hello world, I'm the king of the world!!!
2017-03-08 18:08:00.002  INFO 5328 --- [eduler_Worker-4] c.c.s.quartz.entity.ScheduleTask         : Hello world, 
Copy the code

From the log printing time above, we realized dynamic configuration. At the beginning, the task was executed at 20:30 every day, and then it was executed every 2 minutes through dynamic refresh. Although the above solutions don’t use the Quartz recommended way is perfect, but basically can satisfy our needs, of course, can also adopt the way of triggering event, such as the current client to modify the triggering time of the timing task, asynchronous notification is sent to the background and the background, after receipt of the notification, and then to update the program, also can realize dynamic timing task refreshes