Spring集成Quartz定时任务

        

目录

一 项目参考

二 个人的定时任务实现

1 基本搭建

1.创建一个spring项目

2.pom文件引入必要依赖

2 业务基本实现

2.1 主定时任务

2.2 业务类定时任务

3 Quartz

3.1 任务Job、JobDetail、JobBuilder

3.2 触发器 Trigger、TriggerBuilder

3.3 调度器Scheduler、SchedulerFactory、SchedulerFactoryBean


        公司旧系统需要增加定时任务功能,甲方希望功能简洁且不增加运维人员工作量,因此参考网上案例基于Spring矿机用Quartz写了简单的定时任务模块。

一 项目参考

Spring 集成quartz框架的两种方式_皮卡车厘子的博客-CSDN博客_spring集成quartz

Quartz- Quartz API以及Jobs 和Triggers介绍_小小工匠的博客-CSDN博客_quartz-jobs

定时任务框架Quartz-(一)Quartz入门与Demo搭建_是Guava不是瓜娃的博客-CSDN博客_quartz

Quartz-scheduler 定时器概述、核心 API 与 快速入门_蚩尤后裔的博客-CSDN博客_org.quartz-scheduler

项目使用的是quartz版本是2.2.1,需要提前引入到pom文件中

        <!--核心包-->
        <dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.2.1</version>
		</dependency>
        <!--工具包-->
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz-jobs</artifactId>
			<version>2.2.1</version>
		</dependency>

避免和谐,这里复制一份留着自己看:下面内容基本都是贴的链接里的,因为这部分公司项目代码也是借鉴的模板,所以贴了部分不涉密的模板代码块。

        Java项目中常使用的定时器有JDK Timer、Quartz、Spring Task等三种。Quartz的功能强大,配置也比较复杂,适合大型、多定时任务的项目使用。Spring Task配置较为简单轻量,需要Spring框架支持。JDK自带的定时器Timer使用灵活,配置简单,适合中小型项目。这里记录下quartz方式

一、Quartz作业类的继承方式来讲,可以分为两类:

1.作业类需要继承自特定的作业类基类,如Quartz中需要继承自org.springframework.scheduling.quartz.QuartzJobBean;
2.作业类即普通的java类,不需要继承自任何基类。
使用QuartzJobBean,需要继承,而且在注入Spring容器中其他service时候需要在schedulerContextAsMap中注入,比较麻烦,否则不能成功(具体操作参考:https://blog.csdn.net/whaosy/article/details/6298686)。

使用MethodInvokeJobDetailFactoryBean则需要指定targetObject(任务实例)和targetMethod(实例中要执行的方法),可以方便注入Spring容器中其他的service。后者优点是无侵入,业务逻辑简单。所以我更推荐的第二种!

二、从任务调度的触发时机来分,这里主要是针对作业使用的触发器,主要有以下两种:

每隔指定时间则触发一次,在Quartz中对应的触发器为:org.springframework.scheduling.quartz.SimpleTriggerBean
每到指定时间则触发一次,在Quartz中对应的调度器为:org.springframework.scheduling.quartz.CronTriggerBean
三、下面只保留了原作者写的第二种——作业类不继承特定基类

package com.summer.job;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
import com.summer.opendoor.service.OpenDoorService;
 
@Component
public class JobMethodInvoke{
	
	public JobMethodInvoke() {
		super();
		System.out.println("JobMethodInvoke初始化");
	}
 
	@Autowired
	private OpenDoorService openDoorService;
 
	
	protected void doSomething() {
		System.out.println("JOB任务执行了");
		openDoorService.open();
	}
 
}

注意中间的引用关系,如下图

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
 
	<!--第一步 要执行任务的作业类。 -->
	<bean id="testQuartz" class="com.summer.job.JobMethodInvoke" />
	
	<!-- 第二步 将需要执行的定时任务注入JOB中。 -->
	<bean id="jobDetail"
		class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
		<property name="targetObject" ref="testQuartz"></property>
		<!-- 任务类中需要执行的方法 -->
		<property name="targetMethod" value="doSomething"></property>
		<!-- 上一次未执行完成的,要等待有再执行。 -->
		<property name="concurrent" value="false"></property>
	</bean>
 
	<!--第三步 基本的定时器,会绑定具体的任务。 -->	
	<!-- 第一种 SimpleTriggerBean,只支持按照一定频度调用任务,如每隔30分钟运行一次。配置方式如下: -->
	<!-- <bean id="testTrigger"
		class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
		<property name="jobDetail" ref="jobDetail"></property>
		<property name="startDelay" value="3000"></property>
		<property name="repeatInterval" value="2000"></property>
	</bean> -->	
	
	<!-- 第二种 CronTriggerBean,支持到指定时间运行一次,如每天12:00运行一次等。配置方式如下: -->
	<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
		<property name="jobDetail" ref="jobDetail" />
		<!-- <!—每天12:00运行一次 —> -->
		<property name="cronExpression" value="0 0 12 * * ?" />
	</bean>	
	
	<!-- 第四步 配置调度工厂 -->
	<bean id="schedulerFactoryBean" lazy-init="true"
		class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="triggers">
			<list>
				<!-- <ref bean="testTrigger"></ref> -->
				<ref bean="cronTrigger" />
			</list>
		</property>
	</bean>
</beans>

二 个人的定时任务实现

下面贴一下根据自己项目用到定时任务的简化版本,使用的工具是idea

1 基本搭建

1.创建一个spring项目

2.pom文件引入必要依赖


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">


<config>
    <!-- https://blog.csdn.net/yk614294861/article/details/84324603  参考第二步作业类不需要继承特定基类 -->
    <!-- 任务初始化   第一步 要执行任务的作业类-->
    <bean id="initQuartzTask" class="com.test.batch.scheduler.core.InitQuartzTask">
        <ref name="schedulerFactoryBean">schedulerFactoryBean</ref>
    </bean>
    <!-- 第二步 将需要执行的定时任务注入JOB中 -->
    <bean id="methodInvokingJobDetailFactoryBean" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
		<ref name="targetObject">initQuartzTask</ref>
		<!-- 任务类中需要执行的方法 也就是InitQuartzTask类中 scheduleTask方法-->
		<param name="targetMethod">scheduleTask</param>
    </bean>
    
    <!-- 第三步主定时计划  触发器将job注入    SimpleTriggerBean,只支持按照一定频度调用任务,如每隔x分终运行 -->
    <bean id="managerTriggerBean" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
		<ref name="jobDetail">methodInvokingJobDetailFactoryBean</ref>
		<param name="startDelay">1000</param><!-- 延时1秒执行任务 -->
		<param name="repeatInterval">600000</param><!-- 任务执行周期,每隔 10分钟就启动加载任务执行 -->
    </bean>
    
    <!-- 第四步注册SchedulerFactoryBean 配置调度工厂-->
    <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <param name="applicationContextSchedulerContextKey">applicationContextKey</param>
        <list name="triggers">
            <ref>managerTriggerBean</ref>
        </list>
    </bean>
</config>
</spring:beans>

2 业务基本实现

 

2.1 主定时任务

<bean id="managerTriggerBean" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
		<ref name="jobDetail">methodInvokingJobDetailFactoryBean</ref>
		<param name="startDelay">1000</param><!-- 延时1秒执行任务 -->
		<param name="repeatInterval">600000</param><!-- 任务执行周期,每隔 10分钟就启动加载任务执行 -->
    </bean>

        如图创建了SimpleTriggerFactoryBean类型主定时任务,每10分钟一次读取定时任务表中的数据,经过一定校验后维护调度器。

2.2 业务类定时任务

        1)项目的业务类定时任务及相关配置都存在指定数据库中,大概内容为执行频率、是否允许并发、是否发送短信、禁止运行时间等辅助信息。业务如何运行的则只能通过修改业务代码实现。

        2)如何把定时任务加载到管理器中:大概流程是主定时任务每10分钟一次从定时任务数据表中读取任务批量执行configTask方法,将定时任务加载到Scheduler或进行更新。任务属性包括要数据表唯一主键、Cron表达式表达式、需要执行的任务名等信息。

优点:不需要重启服务就能定时更新任务信息。

缺点:需要通过数据库直接维护定时任务信息,风险较高;(后面补了基本数据录入页面)

           数据库信息修改后需要等待主任务读取数据(间隔为10分钟),不能即刻生效(项目后面进行了优化,可以通过其他方式执行相关任务而不走定时(两个渠道代码逻辑上互不影响,业务上冲突另外解决))。

public static void configTask(ScheduleTask task, Scheduler schedulerFactoryBean) {
		try {
			// 获取定时工厂类
			Scheduler scheduler = schedulerFactoryBean;
			// 根据任务ID获取触发器key
			TriggerKey triggerKey = TriggerKey.triggerKey(task.getTaskId());
			// 获取触发器
			CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
			// 如果没有触发器,则新建触发器
			if(Util.isNullOrEmpty(trigger)) {
				// 如果计划任务状态是启用状态,则创建新的触发器
				if("1".equals(task.getTaskStatus())) {
					createTrigger(task, scheduler);
				}
			}else {
				// Trigger已存在,那么更新相应的定时设置
				updateTrigger(task, scheduler, trigger, triggerKey);
			}
		} catch (SchedulerException e) {
			e.printStackTrace();
		}
	}

图中的task是定时任务信息

public class ScheduleTask {
	/** 任务ID */
    private String taskId;
    /** 任务名称 */
    private String taskName;
    /** 任务分组 */
    private String taskGroup;
    /** 任务状态 0禁用 1启用 2删除*/
    private String taskStatus;
    /** 任务运行时间表达式 */
    private String cronExpression;
    /** 任务描述 */
    private String description;
    /** 任务类 */
    private String targetObject;
    /** 任务方法 */
    private String targetMethod;
    /** 是否并发 0禁用 1启用 */
    private String concurrent;
    /** 任务开始时间 **/
    private Timestamp startTime;
    /**延期时间*/
    private long delayTime;
    /**操作员信息*/
    private String userInfo;
}
private static void createTrigger(ScheduleTask task, Scheduler scheduler) throws SchedulerException {
		// 根据任务是否并行,创建不同的job
		Class clazz = "1".equals(task.getConcurrent()) ? QuartzTaskFactory.class
				: QuartzJobFactoryDisallowConcurrentExecution.class;
		// JobBuilder.newJob(自定义任务类).withIdentity(任务名称,组名).build()
		JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(task.getTaskId()).build();
		jobDetail.getJobDataMap().put("ScheduleTask", task);
		// 获取调度时间,并赋值
		CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(task.getCronExpression());
		CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(task.getTaskId()).withSchedule(scheduleBuilder).build();
		// Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job
		scheduler.scheduleJob(jobDetail, trigger);
		log.info(new Date() + ": 新建" + task.getTaskName() + "计划任务");
	}
private static void updateTrigger(ScheduleTask task, Scheduler scheduler, CronTrigger trigger,TriggerKey triggerKey) throws SchedulerException {
		// 任务为可用状态
		if("1".equals(task.getTaskStatus())) {
			// 如果触发器的任务时间跟数据库中任务的任务时间相同,则不用更新设置
			if(!trigger.getCronExpression().equalsIgnoreCase(task.getCronExpression())) {
				CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(task.getCronExpression());
				// 按新的cronExpression表达式重新构建trigger
				trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
				// 按新的trigger重新设置job执行
				scheduler.rescheduleJob(triggerKey, trigger);
			}
		}else {
			// 不可用  
	        scheduler.pauseTrigger(trigger.getKey());// 停止触发器  
	        scheduler.unscheduleJob(trigger.getKey());// 移除触发器  
	        scheduler.deleteJob(trigger.getJobKey());// 删除任务
		}
	}

3 Quartz

Quartz的核心主要是调度器Scheduler、任务Job、触发器Trigger三部分

Quartz的核心是scheduler接口类,而具体的实现类都是通过SchedulerFactory工厂类实现

Quartz API 主要包含了以下接口

  • Scheduler – 调度器,将多个JobDetail和Trigger注册到Scheduler后,通过Scheduler控制执行
  • Job – 由调度器调度的任务需要实现的接口(需要调度的具体内容)。(主要实现其execute()方法)
  • JobDetail – 用于绑定Job,定义任务的实例,包含了任务调度的方案和策略等属性。
  • Trigger – 用于定义需要执行的任务和任务的执行时间。(包含SimpleTrigger和CronTrigger)
  • JobBuilder – 用于定义/创建 JobDetail 实例。
  • TriggerBuilder – 用于定义/创建 Trigger 对象。

3.1 任务Job、JobDetail、JobBuilder

需要执行的调度任务需要实现Job接口重写execute()方法(Job中也只有这一个方法)

public interface Job {

    void execute(JobExecutionContext context)
        throws JobExecutionException;

}
@DisallowConcurrentExecution
public class QuartzTaskFactory implements Job{
	private static Log log = LogFactory.getLog(QuartzTaskFactory.class);

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		log.info("----------定时任务开始----------");
		ScheduleTask scheduleTask = (ScheduleTask) context.getMergedJobDataMap().get("ScheduleTask");
		try {
			TaskUtils.invokMethod(scheduleTask,TaskUtils.getApplicationContext(context));
		} catch (Exception e) {
			log.error(e.getMessage());
		}
		log.info("----------定时任务结束----------" + scheduleTask.getTaskName());
	}
}

补充:@DisallowConcurrentExecution这个注解不是必须的,他的作用是禁止Quart的并发操作。一般针对于Job任务执行时间大于间隔时间的情况(例如任务间隔为15s,当时执行要30s,不过这种情况建议修改业务或者时间间隔)

创建JobBuilder定义JobDetail,并生成唯一的任务标识(JobKey)

JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(task.getTaskId()).build();
    // JobBuilder类里的JobDetail方法
    public JobDetail build() {

        JobDetailImpl job = new JobDetailImpl();
        
        job.setJobClass(jobClass);
        job.setDescription(description);
        if(key == null)
            key = new JobKey(Key.createUniqueName(null), null);
        job.setKey(key); 
        job.setDurability(durability);
        job.setRequestsRecovery(shouldRecover);
        
        
        if(!jobDataMap.isEmpty())
            job.setJobDataMap(jobDataMap);
        
        return job;
    }

3.2 触发器 Trigger、TriggerBuilder

整体来说触发器是用来告诉调度程序作业什么是否触发的,例如下图中跟时间有关属性、JobKey属性(Trigger绑定的Job实例标识)、是否重复执行、执行间隔等

 常见的Trigger主要是SimpleTrigger和CronTrigger

  • SimpleTrigger:在一个指定时间段内执行一次作业任务或是在指定的时间间隔内多次执行作业任务。
  • CronTrigger:基于日历的作业调度器,而不是像SimpleTrigger那样精确指定间隔时间,因此SimpleTrigger能够实现的,CronTrigger都能够实现,所以普遍都用CronTrigger。

项目使用的就是CronTrigger,先通过CronScheduleBuilder根据Cron表达式生成调度规则,再通过TriggerBuilder构建CronTrigger类型的触发器,最后用scheduler.scheduleJob(jobDetail, trigger);方法将cronTrigger触发器和任务jobDetail绑定。

由上述步骤可以看出,Cron变换后需要重新构建CronScheduleBuilder再构建CronTrigger,然后调用scheduler.rescheduleJob(triggerKey,  CronTrigger);更新触发器才行。其中triggerKey是触发器的Key,是通过TriggerKey.triggerKey(taskId)获取到的。

备注:一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job

3.3 调度器Scheduler、SchedulerFactory、SchedulerFactoryBean

主要方法 启动、暂停、关闭等

项目使用SchedulerFactoryBean在afterPropertiesSet方法里初始化SchedulerFactory,代码里默认生成StdSchedulerFactory这一个(DirectSchedulerFactory可以在代码里定制Schduler 参数)。并且查看相关代码,可以知道还要设置

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。 Quartz的优势: 1、Quartz是一个任务调度框架(库),它几乎可以集成到任何应用系统中。 2、Quartz是非常灵活的,它让您能够以最“自然”的方式来编写您的项目的代码,实现您所期望的行为 3、Quartz是非常轻量级的,只需要非常少的配置 —— 它实际上可以被跳出框架来使用,如果你的需求是一些相对基本的简单的需求的话。 4、Quartz具有容错机制,并且可以在重启服务的时候持久化(”记忆”)你的定时任务,你的任务也不会丢失。 5、可以通过Quartz,封装成自己的分布式任务调度,实现强大的功能,成为自己的产品。6、有很多的互联网公司也都在使用Quartz。比如美团 Spring是一个很优秀的框架,它无缝的集成Quartz,简单方便的让企业级应用更好的使用Quartz进行任务的调度。   课程说明:在我们的日常开发中,各种大型系统的开发少不了任务调度,简单的单机任务调度已经满足不了我们的系统需求,复杂的任务会让程序猿头疼, 所以急需一套专门的框架帮助我们去管理定时任务,并且可以在多台机器去执行我们的任务,还要可以管理我们的分布式定时任务。本课程从Quartz框架讲起,由浅到深,从使用到结构分析,再到源码分析,深入解析QuartzSpring+Quartz,并且会讲解相关原理, 让大家充分的理解这个框架和框架的设计思想。由于互联网的复杂性,为了满足我们特定的需求,需要对Spring+Quartz进行二次开发,整个二次开发过程都会进行讲解。Spring被用在了越来越多的项目中, Quartz也被公认为是比较好用的定时器设置工具,学完这个课程后,不仅仅可以熟练掌握分布式定时任务,还可以深入理解大型框架的设计思想。
好的,我了解了您的问题。在Spring Cloud集成Quartz时,需要进行以下数据库配置: 1. 首先,在您的Spring Boot项目中添加Quartz的依赖项。您可以在pom.xml文件中添加以下依赖项: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> ``` 2. 接下来,在application.properties文件中配置Quartz的数据库属性,例如: ``` spring.quartz.job-store-type=jdbc spring.quartz.jdbc.initialize-schema=always spring.datasource.url=jdbc:mysql://localhost:3306/quartz spring.datasource.username=root spring.datasource.password=password spring.datasource.driver-class-name=com.mysql.jdbc.Driver ``` 其中,spring.quartz.job-store-type设置为jdbc,表示Quartz将使用数据库存储作业和触发器,spring.quartz.jdbc.initialize-schema设置为always表示每次启动应用程序时都会初始化Quartz数据库,spring.datasource.url、spring.datasource.username、spring.datasource.password和spring.datasource.driver-class-name为您的数据库连接属性。 3. 最后,定义您的定时任务,例如: ``` @Configuration public class QuartzConfiguration { @Bean public JobDetail myJobDetail() { return JobBuilder.newJob(MyJob.class) .withIdentity("myJob") .storeDurably() .build(); } @Bean public Trigger myTrigger() { return TriggerBuilder.newTrigger() .forJob(myJobDetail()) .withIdentity("myTrigger") .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")) .build(); } } @Component public class MyJob implements Job { @Override public void execute(JobExecutionContext context) { // 任务逻辑 } } ``` 其中,定时任务使用Cron表达式配置,该表达式将在每分钟的第0秒开始,每5秒执行一次任务。 以上就是Spring Cloud集成Quartz数据库配置定时任务的方法。希望能够帮助到您!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值