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-poolorg.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPoolorg.quartz.threadPool.threadCount=2# job-storeorg.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@NoArgsConstructorpublic 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/
Spring Boot整合Quartz实现定时任务表配置的更多相关文章
- spring boot 整合 quartz 集群环境 实现 动态定时任务配置【原】
最近做了一个spring boot 整合 quartz 实现 动态定时任务配置,在集群环境下运行的 任务.能够对定时任务,动态的进行增删改查,界面效果图如下: 1. 在项目中引入jar 2. 将需要 ...
- spring boot整合quartz实现多个定时任务
版权声明:本文为博主原创文章,转载请注明出处. https://blog.csdn.net/liuchuanhong1/article/details/78543574 最近收到了很多封邮件, ...
- spring boot 整合quartz ,job不能注入的问题
在使用spring boot 整合quartz的时候,新建定时任务类,实现job接口,在使用@AutoWire或者@Resource时,运行时出现nullpointException的问题.显然是相关 ...
- Spring Boot 整合Quartz定时器
概述 项目需要定时器的调度管理,原来使用Spring Boot自带的定时器,但是不能后台动态的操作暂停.启动以及新增任务等操作,维护起来相对麻烦:最近研究了Quartz的框架,觉得还算不错,整理了一下 ...
- Spring Boot集成quartz实现定时任务并支持切换任务数据源
org.quartz实现定时任务并自定义切换任务数据源 在工作中经常会需要使用到定时任务处理各种周期性的任务,org.quartz是处理此类定时任务的一个优秀框架.随着项目一点点推进,此时我们并不满足 ...
- spring boot整合quartz定时任务案例
1.运行环境 开发工具:intellij idea JDK版本:1.8 项目管理工具:Maven 4.0.0 2.GITHUB地址 https://github.com/nbfujx/springBo ...
- 【Spring Boot学习之六】Spring Boot整合定时任务&异步调用
环境 eclipse 4.7 jdk 1.8 Spring Boot 1.5.2一.定时任务1.启动类添加注解@EnableScheduling 用于开启定时任务 package com.wjy; i ...
- Spring+Quartz 实现定时任务的配置方法
Spring+Quartz 实现定时任务的配置方法 整体介绍 一.Quartz介绍 在企业应用中,我们经常会碰到时间任务调度的需求,比如每天凌晨生成前天报表,每小时生成一次汇总数据等等.Quartz是 ...
- Spring Boot 整合 Freemarker,50 多行配置是怎么省略掉的?
Spring Boot2 系列教程接近完工,最近进入修修补补阶段.Freemarker 整合貌似还没和大家聊过,因此今天把这个补充上. 已经完工的 Spring Boot2 教程,大家可以参考这里: ...
随机推荐
- 图像边缘检测--OpenCV之cvCanny函数
图像边缘检测--OpenCV之cvCanny函数 分类: C/C++ void cvCanny( const CvArr* image, CvArr* edges, double threshold1 ...
- Linux常用命令(第二版) --权限管理命令
权限管理命令 1.chmod[change the permissions mode of a file] : /bin/chmod 语法: chmod [{ugo}{+-=}{rwx}] [文件或目 ...
- WebApplicationContext初始化
Spring 提供了用于启动WebApplicaionContext的Web容器监听器. 通过Web容器监听器引导: <!-- 1 指定配置文件 --> <context-param ...
- 百度站长平台MIP
使用说明 MIP(Mobile Instant Pages - 移动网页加速器),是一套应用于移动网页的开放性技术标准.通过提供 MIP-HTML 规范.MIP-JS 运行环境以及 MIP-Cache ...
- MOOS学习笔记1——HelloWorld
MOOS学习笔记1--HelloWorld 例程 /* * @功能:通讯客户端的最简单程序,向MOOSDB发送名为"Greeting" * 数据"Hello", ...
- java中文拼音字母排序
package com.yputil.util; import java.text.CollationKey;import java.text.Collator;import java.util.Ar ...
- valid sudoku(数独)
Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules. The Sudoku board could be ...
- Shell排序(改良的插入排序)
Shell排序算法最初是由D.L Shell于1959年提出,假设要排序的元素有n个,则每个进行插入排序是并不是所偶的元素同时进行,而是去一段间隔. Shell首先将间隔设定为n/2,然后跳跃的进行插 ...
- oracle超出打开游标的最大数的原因和解决方案
oracle超出打开游标的最大数的原因和解决方案 分类: Oracle相关2012-06-05 10:36 6362人阅读 评论(0) 收藏 举报 oracle数据库sqljavasessionsys ...
- Django之Apps源码学习
先了解下官方文档的介绍 Django包含了一个已经安装应用的注册表,这个注册表存储着配置信息以及用来自省,同时也维护着可用模型的列表. 这个注册表就是apps,位于django.apps下,本质上是一 ...