Spring Boot整合Quartz实现定时任务表配置

最近有个小项目要做,spring mvc下的task设置一直不太灵活,因此在Spring Boot上想做到灵活的管理定时任务。需求就是,当项目启动的时候,如果有定时任务则加载进来,生成scheduler,通过后台表配置可以随时更新定时任务状态(启动、更改、删除)。

添加依赖


<!-- spring's support for quartz -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context-support</artifactId>

</dependency>

<!--quartz-->

<dependency>

<groupId>org.quartz-scheduler</groupId>

<artifactId>quartz</artifactId>

<version>2.2.3</version>

</dependency>

一个是Spring框架的支持,一个是Quartz的依赖,有的博客会加上quartz-jobs,在当前示例中没有用到,这里不做添加。

调整配置

  • application.properties增加参数


    #quartz enabled 设置在当前项目是否运行quartz定时任务

    quartz.enabled=true

  • 增加quartz配置文件quartz.properties

    # thread-pool

    org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool

    org.quartz.threadPool.threadCount=2

    # job-store

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

    这些参数可以不设置,有一些默认值,threadCount的默认值是10,spring mvc下定时任务的默认值是1,所以如果某个定时任务卡住了,肯会影响其后的多个定时任务的执行。

任务表配置

  • Entity

    实体类,这里是JobConfig,这里没有做过多的设计,只是实现了cron类型的定时任务及任务状态,fullEntity是执行任务的类全名,如我们用的com.example.demo.jobs.MyJob


    import lombok.AllArgsConstructor;

    import lombok.Data;

    import lombok.NoArgsConstructor;

     

    import javax.persistence.Entity;

    import javax.persistence.GeneratedValue;

    import javax.persistence.GenerationType;

    import javax.persistence.Id;

    import java.util.Date;

     

    /**

    * Created by Administrator on 2017/8/25.

    */

    @Entity

    @Data

    @AllArgsConstructor

    @NoArgsConstructor

    public class JobConfig {

    @Id

    @GeneratedValue(strategy = GenerationType.AUTO)

    private Integer id;

    private String name;

    private String fullEntity;

    private String groupName;

    private String cronTime;

    private Integer status;

    private Date createAt;

    private Date updateAt;

    }

    代码没有set/get是因为使用了lombok,@Data注解实现set/get/toString等工作

  • Repository

    这里主要是定义了一个根据定时任务状态获取对应的定时任务的方法,JobConfigRepository


import com.example.demo.dto.JobConfig;

import org.springframework.data.jpa.repository.JpaRepository;

 

import java.util.List;

 

/**

* Created by Administrator on 2017/8/25.

*/

public interface JobConfigRepository extends JpaRepository<JobConfig, Integer> {

List<JobConfig> findAllByStatus(int status);

}

  • Service

    调用Repository的方法,提供查询,JobConfigService


import com.example.demo.dto.JobConfig;

import com.example.demo.repositories.JobConfigRepository;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

 

import java.util.List;

 

/**

* Created by Administrator on 2017/8/25.

*/

@Service

public class JobConfigService {

@Autowired

private JobConfigRepository jobConfigRepository;

 

public List<JobConfig> findAllByStatus(Integer status) {

return jobConfigRepository.findAllByStatus(status);

}

}

添加自动注入支持

源于https://gist.github.com/jelies/5085593的解决方案,解决的问题就是在org.quartz.Job的子类里无法直接使用Service等依赖,具体如下:


import org.quartz.spi.TriggerFiredBundle;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.config.AutowireCapableBeanFactory;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

import org.springframework.scheduling.quartz.SpringBeanJobFactory;

 

/**

* Adds auto-wiring support to quartz jobs.

* @see "https://gist.github.com/jelies/5085593"

*/

public final class AutoWiringSpringBeanJobFactory extends SpringBeanJobFactory

implements ApplicationContextAware {

 

private transient AutowireCapableBeanFactory beanFactory;

 

public void setApplicationContext(ApplicationContext applicationContext)

throws BeansException {

beanFactory = applicationContext.getAutowireCapableBeanFactory();

}

 

@Override

protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {

 

final Object job = super.createJobInstance(bundle);

beanFactory.autowireBean(job);

return job;

}

 

}

Scheduler工具类

实现Scheduler的增删改功能以及JobDetail、CronTrigger的创建,需要注意,这里的数据都是源于JobConfig这个表,name是FullEntity+Id拼接而成的。具体看代码可知:


import com.example.demo.config.AutoWiringSpringBeanJobFactory;

import com.example.demo.dto.JobConfig;

import org.quartz.*;

import org.quartz.impl.StdSchedulerFactory;

import org.springframework.context.ApplicationContext;

import org.springframework.core.io.ClassPathResource;

import org.springframework.scheduling.quartz.CronTriggerFactoryBean;

import org.springframework.scheduling.quartz.JobDetailFactoryBean;

import org.springframework.scheduling.quartz.SchedulerFactoryBean;

 

import java.text.ParseException;

 

public class SchedulerUtil {

 

//定时任务Scheduler的工厂类,Quartz提供

private static StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();

//CronTrigger的工厂类

private static CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();

//JobDetail的工厂类

private static JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean();

//自动注入Spring Bean的工厂类

private static AutoWiringSpringBeanJobFactory jobFactory =

new AutoWiringSpringBeanJobFactory();

//定时任务Scheduler的工厂类,Spring Framework提供

private static SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();

 

static {

//加载指定路径的配置

schedulerFactoryBean.setConfigLocation(new ClassPathResource("quartz.properties"));

}

 

/**

* 创建定时任务,根据参数,创建对应的定时任务,并使之生效

* @param config

* @param context

* @return

*/

public static boolean createScheduler(JobConfig config,

ApplicationContext context) {

try {

//创建新的定时任务

return create(config, context);

} catch (Exception e) {

e.printStackTrace();

}

return false;

}

 

/**

* 删除旧的定时任务,创建新的定时任务

* @param oldConfig

* @param config

* @param context

* @return

*/

public static Boolean modifyScheduler(JobConfig oldConfig,JobConfig config,

ApplicationContext context) {

if (oldConfig == null || config == null || context == null) {

return false;

}

try {

String oldJobClassStr = oldConfig.getFullEntity();

String oldName = oldJobClassStr + oldConfig.getId();

String oldGroupName = oldConfig.getGroupName();

//1、清除旧的定时任务

delete(oldName, oldGroupName);

//2、创建新的定时任务

return create(config, context);

} catch (SchedulerException e) {

e.printStackTrace();

} catch (Exception e) {

e.printStackTrace();

}

return false;

}

 

/**

* 提取的删除任务的方法

* @param oldName

* @param oldGroupName

* @return

* @throws SchedulerException

*/

private static Boolean delete(String oldName, String oldGroupName)

throws SchedulerException {

TriggerKey key = new TriggerKey(oldName, oldGroupName);

Scheduler oldScheduler = schedulerFactory.getScheduler();

//根据TriggerKey获取trigger是否存在,如果存在则根据key进行删除操作

Trigger keyTrigger = oldScheduler.getTrigger(key);

if (keyTrigger != null) {

oldScheduler.unscheduleJob(key);

}

return true;

}

 

/**

* 提取出的创建定时任务的方法

* @param config

* @param context

* @return

*/

private static Boolean create(JobConfig config, ApplicationContext context) {

try {

//创建新的定时任务

String jobClassStr = config.getFullEntity();

Class clazz = Class.forName(jobClassStr);

String name = jobClassStr + config.getId();

String groupName = config.getGroupName();

String description = config.toString();

String time = config.getCronTime();

 

JobDetail jobDetail = createJobDetail(clazz, name, groupName, description);

if (jobDetail == null) {

return false;

}

Trigger trigger = createCronTrigger(jobDetail,

time, name, groupName, description);

if (trigger == null) {

return false;

}

 

jobFactory.setApplicationContext(context);

 

schedulerFactoryBean.setJobFactory(jobFactory);

schedulerFactoryBean.setJobDetails(jobDetail);

schedulerFactoryBean.setTriggers(trigger);

schedulerFactoryBean.afterPropertiesSet();

Scheduler scheduler = schedulerFactoryBean.getScheduler();

if (!scheduler.isShutdown()) {

scheduler.start();

}

return true;

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (SchedulerException e) {

e.printStackTrace();

} catch (Exception e) {

e.printStackTrace();

}

return false;

}

 

/**

* 根据指定的参数,创建JobDetail

* @param clazz

* @param name

* @param groupName

* @param description

* @return

*/

public static JobDetail createJobDetail(Class clazz, String name,

String groupName, String description) {

jobDetailFactory.setJobClass(clazz);

jobDetailFactory.setName(name);

jobDetailFactory.setGroup(groupName);

jobDetailFactory.setDescription(description);

jobDetailFactory.setDurability(true);

jobDetailFactory.afterPropertiesSet();

return jobDetailFactory.getObject();

}

 

/**

* 根据参数,创建对应的CronTrigger对象

*

* @param job

* @param time

* @param name

* @param groupName

* @param description

* @return

*/

public static CronTrigger createCronTrigger(JobDetail job, String time,

String name, String groupName, String description) {

factoryBean.setName(name);

factoryBean.setJobDetail(job);

factoryBean.setCronExpression(time);

factoryBean.setDescription(description);

factoryBean.setGroup(groupName);

try {

factoryBean.afterPropertiesSet();

} catch (ParseException e) {

e.printStackTrace();

}

return factoryBean.getObject();

}

}

Scheduler初始化配置

通过Spring Boot的@Configuration及@ConditionalOnExpression(“‘${quartz.enabled}’==’true’”)实现初始化时是否加载项目的定时任务——SchedulerConfig,这里的参数quartz.enabled的值即是我们上面在配置文件里配置的。代码如下:


package com.example.demo.config;

 

import com.example.demo.dto.JobConfig;

import com.example.demo.service.JobConfigService;

import com.example.demo.util.SchedulerUtil;

import org.quartz.impl.StdSchedulerFactory;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;

import org.springframework.context.ApplicationContext;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

 

import java.util.List;

 

@Configuration

@ConditionalOnExpression("'${quartz.enabled}'=='true'")

public class SchedulerConfig {

Logger logger = LoggerFactory.getLogger(getClass());

@Autowired

private ApplicationContext applicationContext;

 

@Autowired

private JobConfigService jobConfigService;

 

@Bean

public StdSchedulerFactory stdSchedulerFactory() {

StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();

//获取JobConfig集合

List<JobConfig> configs = jobConfigService.findAllByStatus(1);

logger.debug("Setting the Scheduler up");

for (JobConfig config : configs) {

try {

Boolean flag = SchedulerUtil.createScheduler(

config, applicationContext);

System.out.println("执行结果:" + (flag == true ? "成功" : "失败"));

} catch (Exception e) {

e.printStackTrace();

}

}

return stdSchedulerFactory;

}

}

Job实现

这里定义了一个简单的Job继承org.quartz.Job,主要是查询当前的定时任务表配置数据,MyJob,具体代码如下:


package com.example.demo.jobs;

 

import com.example.demo.dto.JobConfig;

import com.example.demo.service.JobConfigService;

import org.quartz.Job;

import org.quartz.JobExecutionContext;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

 

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.List;

 

@Component

public class MyJob implements Job {

@Autowired

private JobConfigService jobConfigService;

 

public void execute(JobExecutionContext context) {

System.out.println();

System.out.println();

//是哪个定时任务配置在执行,可以看到,因为在前面我们将描述设置为了配置类的toString结果

System.out.println(context.getJobDetail().getDescription());

SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

System.out.println(this.toString() + ":" + f.format(new Date()) +

"正在执行Job executing...");

List<JobConfig> configs = jobConfigService.findAllByStatus(1);

for (JobConfig config : configs) {

System.out.println(config.toString());

}

}

}

数据库表job_config

  • 表创建

CREATE TABLE `job_config` (

`id` INT(11) NOT NULL AUTO_INCREMENT,

`create_at` DATETIME DEFAULT NULL,

`cron_time` VARCHAR(255) DEFAULT NULL,

`full_entity` VARCHAR(255) DEFAULT NULL,

`group_name` VARCHAR(255) DEFAULT NULL,

`name` VARCHAR(255) DEFAULT NULL,

`status` INT(11) DEFAULT NULL,

`update_at` DATETIME DEFAULT NULL,

PRIMARY KEY (`id`)

)

ENGINE = InnoDB

AUTO_INCREMENT = 3

DEFAULT CHARSET = utf8;

  • 表数据

/*Data for the table `job_config` */

 

INSERT INTO `job_config` (`id`, `create_at`, `cron_time`, `full_entity`, `group_name`, `name`, `status`, `update_at`)

VALUES (1, '2017-08-25 21:03:35', '0/8 * * * * ?', 'com.example.demo.jobs.MyJob', 'test', 'My test', 1, NULL),

(2, '2017-08-25 21:12:02', '0/23 * * * * ?', 'com.example.demo.jobs.MyJob', 'test', 'My Job', 1, NULL);

运行结果


JobConfig(id=1, name=My test, fullEntity=com.example.demo.jobs.MyJob, groupName=test, cronTime=0/8 * * * * ?, status=1, createAt=2017-08-25 21:03:35.0, updateAt=null)

com.example.demo.jobs.MyJob@7602fe69:2017-08-26 10:43:16正在执行Job executing...

Hibernate: select jobconfig0_.id as id1_1_, jobconfig0_.create_at as create_a2_1_, jobconfig0_.cron_time as cron_tim3_1_, jobconfig0_.full_entity as full_ent4_1_, jobconfig0_.group_name as group_na5_1_, jobconfig0_.name as name6_1_, jobconfig0_.status as status7_1_, jobconfig0_.update_at as update_a8_1_ from job_config jobconfig0_ where jobconfig0_.status=?

JobConfig(id=1, name=My test, fullEntity=com.example.demo.jobs.MyJob, groupName=test, cronTime=0/8 * * * * ?, status=1, createAt=2017-08-25 21:03:35.0, updateAt=null)

JobConfig(id=2, name=My Job, fullEntity=com.example.demo.jobs.MyJob, groupName=test, cronTime=0/23 * * * * ?, status=1, createAt=2017-08-25 21:12:02.0, updateAt=null)

 

 

JobConfig(id=2, name=My Job, fullEntity=com.example.demo.jobs.MyJob, groupName=test, cronTime=0/23 * * * * ?, status=1, createAt=2017-08-25 21:12:02.0, updateAt=null)

com.example.demo.jobs.MyJob@4a49530:2017-08-26 10:43:23正在执行Job executing...

Hibernate: select jobconfig0_.id as id1_1_, jobconfig0_.create_at as create_a2_1_, jobconfig0_.cron_time as cron_tim3_1_, jobconfig0_.full_entity as full_ent4_1_, jobconfig0_.group_name as group_na5_1_, jobconfig0_.name as name6_1_, jobconfig0_.status as status7_1_, jobconfig0_.update_at as update_a8_1_ from job_config jobconfig0_ where jobconfig0_.status=?

JobConfig(id=1, name=My test, fullEntity=com.example.demo.jobs.MyJob, groupName=test, cronTime=0/8 * * * * ?, status=1, createAt=2017-08-25 21:03:35.0, updateAt=null)

JobConfig(id=2, name=My Job, fullEntity=com.example.demo.jobs.MyJob, groupName=test, cronTime=0/23 * * * * ?, status=1, createAt=2017-08-25 21:12:02.0, updateAt=null)

到这里,Spring Boot与quartz的整合已经完成了,可以通过配置表job_config以及配置quartz.enabled参数来灵活使用定时任务了!

后续还会继续实践、丰富这个示例,如果上文有什么问题,欢迎留言指正,谢谢!

源码:https://github.com/icnws/spring-data-jpa-demo



定时任务的路还有很长,想更灵活?可能需要elastic-job等框架吧!欢迎留言交流!

http://www.icnws.com/2017/145-spring-boot-quartz-editable/

 

时间: 2024-09-19 10:12:35

Spring Boot整合Quartz实现定时任务表配置的相关文章

Spring Boot 整合 Elasticsearch,实现 function score query 权重分查询

摘要: 原创出处 www.bysocket.com 「泥瓦匠BYSocket 」欢迎转载,保留摘要,谢谢! 『 预见未来最好的方式就是亲手创造未来 – <史蒂夫·乔布斯传> 』 运行环境:JDK 7 或 8,Maven 3.0+技术栈:SpringBoot 1.5+,ElasticSearch 2.3.2 本文提纲 一.ES 的使用场景 二.运行 springboot-elasticsearch 工程 三.springboot-elasticsearch 工程代码详解 一.ES 的使用场景 简

Spring 3整合Quartz 2实现定时任务(转)

http://www.meiriyouke.net/?p=82 最近工作中需要用到定时任务的功能,虽然Spring3也自带了一个轻量级的定时任务实现,但感觉不够灵活,功能也不够强大.在考虑之后,决定整合更为专业的Quartz来实现定时任务功能. 首先,当然是添加依赖的jar文件,我的项目是maven管理的,以下的我项目的依赖: <dependencies> <dependency> <groupId>org.springframework</groupId>

Spring 3整合Quartz 2实现手动设置定时任务:新增,修改,删除,暂停和恢复----每一个你不满意的当下,都有一个你不曾努力的过去

  摘要:在项目的管理功能中,对定时任务的管理有时会很常见.但一般定时任务配置都在xml中完成,包括cronExpression表达式,十分的方便.但是如果我的任务信息是保存在数据库的,想要动态的初始化,还有就是任务较多的时候不是得有一大堆的xml配置?或者说我要修改一下trigger的表达式,使原来5秒运行一次的任务变成10秒运行一次,或者说我要控制定时任务的 " 暂停 " 呢?暂停之后又要在某个时间点 " 重启 " 该定时任务呢?或者说直接 " 删除 

Spring Boot 整合 Thymeleaf 完整 Web 案例

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! Thymeleaf 是一种模板语言.那模板语言或模板引擎是什么?常见的模板语言都包含以下几个概念:数据(Data).模板(Template).模板引擎(Template Engine)和结果文档(Result Documents). 数据 数据是信息的表现形式和载体,可以是符号.文字.数字.语音.图像.视频等.数据和信息是不可分离的,数据是信息的表达,信息是数据的内涵.数据本身没有意义,数据只

Spring Boot整合Thymeleaf完整Web案例

Thymeleaf 是一种模板语言.那模板语言或模板引擎是什么?常见的模板语言都包含以下几个概念:数据(Data).模板(Template).模板引擎(Template Engine)和结果文档(Result Documents). 数据 数据是信息的表现形式和载体,可以是符号.文字.数字.语音.图像.视频等.数据和信息是不可分离的,数据是信息的表达,信息是数据的内涵.数据本身没有意义,数据只有对实体行为产生影响时才成为信息. 模板 模板,是一个蓝图,即一个与类型无关的类.编译器在使用模板时,会

Spring Boot 整合 Redis 实现缓存操作

一.缓存的应用场景 二.更新缓存的策略 三.运行 springboot-mybatis-redis 工程案例 四.springboot-mybatis-redis 工程代码配置详解 运行环境: Mac OS 10.12.x JDK 8 + Redis 3.2.8 Spring Boot 1.5.1.RELEASE 一.缓存的应用场景 什么是缓存? 在互联网场景下,尤其2C端大流量场景下,需要将一些经常展现和不会频繁变更的数据,存放在存取速率更快的地方.缓存就是一个存储器,在技术选型中,常用 Re

Spring 3整合Quartz 2实现定时任务二:动态添加任务

前面,我们已经对Spring 3和Quartz 2用配置文件的方式进行了整合,如果需求比较简单的话应该已经可以满足了.但是很多时候,我们常常会遇到需要动态的添加或修改任务,而spring中所提供的定时任务组件却只能够通过修改xml中trigger的配置才能控制定时任务的时间以及任务的启用或停止,这在带给我们方便的同时也失去了动态配置任务的灵活性.我搜索了一些网上的解决方法,都没有很好的解决这个问题,而且大多数提到的解决方案都停留在Quartz 1.x系列版本上,所用到的代码和API已经不能适用于

Spring Boot 整合 Mybatis 实现 Druid 多数据源详解

本文提纲一.多数据源的应用场景二.运行 springboot-mybatis-mutil-datasource 工程案例三.springboot-mybatis-mutil-datasource 工程代码配置详解 一.多数据源的应用场景 目前,业界流行的数据操作框架是 Mybatis,那 Druid 是什么呢? Druid 是 Java 的数据库连接池组件.Druid 能够提供强大的监控和扩展功能.比如可以监控 SQL ,在监控业务可以查询慢查询 SQL 列表等.Druid 核心主要包括三部分:

Spring 3整合Quartz 2实现定时任务一:常规整合

最近工作中需要用到定时任务的功能,虽然Spring3也自带了一个轻量级的定时任务实现,但感觉不够灵活,功能也不够强大.在考虑之后,决定整合更为专业的Quartz来实现定时任务功能. 首先,当然是添加依赖的jar文件,我的项目是maven管理的,以下的我项目的依赖: <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core&l