Spring Boot2.x + Quartz 定时任务模块

Posted 在奋斗的大道

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot2.x + Quartz 定时任务模块相关的知识,希望对你有一定的参考价值。

介绍       

项目是为定时任务模块,可以作为熟悉了解定时任务框架项目也可以将本模块直接移植进自己的项目中

软件涉及技术

SpringBoot2.x + MyBatis3 +MyBatis3-Plus + mysql8 + Quartz(定时任务框架) +Swagger2(接口文档框架) 

效果截图

 

 

项目整体结构: 

 

 项目重点细节说明

 项目之pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.zzg</groupId>
	<artifactId>sb-Quartz</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.2.RELEASE</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<!-- 集中定义管理依赖版本号 -->
		<commons-lang.version>2.6</commons-lang.version>
		<commons-codec.version>1.10</commons-codec.version>
		<commons-lang3.version>3.9</commons-lang3.version>
		<commons-net.version>3.6</commons-net.version>
		<commons-io.version>2.6</commons-io.version>
		<commons-collections.version>3.2.1</commons-collections.version>
		<commons-text.version>1.8</commons-text.version>
		<common-fileupload.version>1.3.1</common-fileupload.version>
		<servlet-api.version>3.1.0</servlet-api.version>
		<httpclient.version>4.5.2</httpclient.version>
		<fastjson.version>1.2.48</fastjson.version>
	</properties>

	<dependencies>
		<!--starter -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<!-- test -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!--web -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- quartz -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-quartz</artifactId>
		</dependency>

		<!-- 数据库连接池druid -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.1.10</version>
		</dependency>
		<!--mysql 驱动程序 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>

		<!-- Swagger2 集成 -->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.7.0</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.7.0</version>
		</dependency>

		<!-- 数据库文档自动生成 -->
		<dependency>
			<groupId>cn.smallbun.screw</groupId>
			<artifactId>screw-core</artifactId>
			<version>1.0.5</version>
		</dependency>
		<dependency>
			<groupId>org.freemarker</groupId>
			<artifactId>freemarker</artifactId>
			<version>2.3.30</version>
		</dependency>

		<!--lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<!-- mybatis-plus 集成 -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.4.1</version>
		</dependency>
		<!--validation -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-annotations</artifactId>
			<version>2.9.0</version>
		</dependency>

		<!--common-lang 常用工具包 -->
		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>${commons-lang.version}</version>
		</dependency>
		<!--commons-lang3 工具包 -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>${commons-lang3.version}</version>
		</dependency>

		<!--commons-codec 加密工具包 -->
		<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
			<version>${commons-codec.version}</version>
		</dependency>
		<!--commons-net 网络工具包 -->
		<dependency>
			<groupId>commons-net</groupId>
			<artifactId>commons-net</artifactId>
			<version>${commons-net.version}</version>
		</dependency>
		<!--common-io 工具包 -->
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>${commons-io.version}</version>
		</dependency>
		<!--common-collection 工具包 -->
		<dependency>
			<groupId>commons-collections</groupId>
			<artifactId>commons-collections</artifactId>
			<version>${commons-collections.version}</version>
		</dependency>
		<!--common-fileupload 工具包 -->
		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>${common-fileupload.version}</version>
		</dependency>
		<!-- common-text 工具包 -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-text</artifactId>
			<version>${commons-text.version}</version>
		</dependency>
		<!-- 集成servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>${servlet-api.version}</version>
		</dependency>
		<!-- 集成Apache HttpClient -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>${httpclient.version}</version>
		</dependency>
		<!-- 集成fastjson -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>${fastjson.version}</version>
		</dependency>
	</dependencies>

</project>

 数据库 schedule_job

CREATE TABLE `schedule_job` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '任务id',
  `job_name` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '任务名称',
  `cron_expression` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT 'cron表达式',
  `bean_name` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '服务名称',
  `method_name` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '方法名称',
  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态 0 启动 1 停止',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '软删除标记 0 正常 1 删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='定时任务';

 schedule_job 表对应的类

 com.zzg.entity.ScheduleJob

com.zzg.mapper.ScheduleJobMapper

com.zzg.service.ScheduleJobService

com.zzg.service.impl.ScheduleJobServiceImpl

ScheduleJobMapper.xml

 定时任务配置对象

package com.zzg.config;

import org.quartz.Scheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;


/**
 * 定时任务配置对象
 * @author zzg
 *
 */
public class QuartzConfig {
	@Bean
    public SchedulerFactoryBean schedulerFactoryBean(){
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        //覆盖已存在的任务
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        //延时60秒启动定时任务,避免系统未完全启动却开始执行定时任务的情况
        schedulerFactoryBean.setStartupDelay(60);
        return schedulerFactoryBean;
    }

    /** 创建schedule **/
    @Bean(name = "scheduler")
    public Scheduler scheduler() {
        return schedulerFactoryBean().getScheduler();
    }
}

枚举类:JobOperateEnum.java

package com.zzg.enums;

import java.io.Serializable;

public enum  JobOperateEnum {
	START(0, "启动"),
    PAUSE(1, "暂停"),
    DELETE(2, "删除");

    private final Integer value;
    private final String desc;

    JobOperateEnum(final Integer value, final String desc) {
        this.value = value;
        this.desc = desc;
    }

    public Serializable getValue() {
        return this.value;
    }

    public String getDesc() {
        return this.desc;
    }

    public String getEnumName() {
        return name();
    }
}

定时任务工厂类

package com.zzg.quartz;

import java.lang.reflect.Method;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import com.zzg.entity.ScheduleJob;
import com.zzg.util.SpringContextUtil;

public class QuartzFactory implements Job {

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		// TODO Auto-generated method stub
		// 获取调度数据
		ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");

		// 获取对应的Bean
		Object object = SpringContextUtil.getBean(scheduleJob.getBeanName());
		try {
			// 利用反射执行对应方法
			Method method = object.getClass().getMethod(scheduleJob.getMethodName());
			method.invoke(object);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

定时任务服务层 IQuartzService.java

package com.zzg.service;

import org.quartz.SchedulerException;

import com.zzg.entity.ScheduleJob;
import com.zzg.enums.JobOperateEnum;

public interface IQuartzService {
	/**
     * 服务器启动执行定时任务
     */
    void timingTask();

    /**
     * 新增定时任务
     * @param job 任务
     */
    void addJob(ScheduleJob job);

    /**
     * 操作定时任务
     * @param jobOperateEnum 操作枚举
     * @param job 任务
     */
    void operateJob(JobOperateEnum jobOperateEnum, ScheduleJob job) throws SchedulerException;

    /**
     * 启动所有任务
     */
    void startAllJob() throws SchedulerException;

    /**
     * 暂停所有任务
     */
    void pauseAllJob() throws SchedulerException;
}

定时任务服务层实现类 QuartzServiceImpl.java

package com.zzg.service.impl;

import java.util.List;

import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.zzg.entity.ScheduleJob;
import com.zzg.enums.JobOperateEnum;
import com.zzg.quartz.QuartzFactory;
import com.zzg.service.IQuartzService;
import com.zzg.service.ScheduleJobService;

@Service
public class QuartzServiceImpl implements IQuartzService {
	/**
	 * 调度器
	 */
	@Autowired
	private Scheduler scheduler;

	@Autowired
	private ScheduleJobService scheduleJobService;

	@Override
	public void timingTask() {
		// TODO Auto-generated method stub
		// 查询数据库是否存在需要定时的任务
		List<ScheduleJob> scheduleJobs = scheduleJobService.list();
		if (scheduleJobs != null) {
			scheduleJobs.forEach(this::addJob);
		}
	}

	@Override
	public void addJob(ScheduleJob job) {

		try {
			// 创建触发器
			Trigger trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName())
					.withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression())).startNow().build();

			// 创建任务
			JobDetail jobDetail = JobBuilder.newJob(QuartzFactory.class).withIdentity(job.getJobName()).build();

			// 传入调度的数据,在QuartzFactory中需要使用
			jobDetail.getJobDataMap().put("scheduleJob", job);

			// 调度作业
			scheduler.scheduleJob(jobDetail, trigger);

		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void operateJob(JobOperateEnum jobOperateEnum, ScheduleJob job) throws SchedulerException {
		// TODO Auto-generated method stub
		JobKey jobKey = new JobKey(job.getJobName());
		JobDetail jobDetail = scheduler.getJobDetail(jobKey);
		if (jobDetail == null) {
			// 抛异常
		}
		switch (jobOperateEnum) {
		case START:
			scheduler.resumeJob(jobKey);
			break;
		case PAUSE:
			scheduler.pauseJob(jobKey);
			break;
		case DELETE:
			scheduler.deleteJob(jobKey);
			break;
		}
	}

	@Override
	public void startAllJob() throws SchedulerException {
		// TODO Auto-generated method stub
		 scheduler.start();
	}

	@Override
	public void pauseAllJob() throws SchedulerException {
		// TODO Auto-generated method stub
		 scheduler.standby();
	}

}

JobSchedule.java 实现开机自运行

package com.zzg.quartz.components;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import com.zzg.service.IQuartzService;

/**
 * 项目启动后,后台任务自带加载运行
 * 
 * @author zzg
 *
 */
@Component
public class JobSchedule implements CommandLineRunner {
	@Autowired
	private IQuartzService taskService;

	@Override
	public void run(String... args) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("任务调度开始==============任务调度开始");
		taskService.timingTask();
		System.out.println("任务调度结束==============任务调度结束");
	}

}

动态定时任务功能实现

静态部分

顾名思义,静态部分就是写一个方法,数据库录入一条数据,当项目启动的时候,就会触发定时任务,等到了时间就会运行。

package com.zzg.quartz.job;

import java.util.Date;

import org.springframework.stereotype.Component;

@Component("JobTest")
public class LogTest {
	public void log() {
		System.out.println("-------------------Log任务执行开始-------------------");
		System.out.println(new Date().toLocaleString());
		System.out.println("-------------------Log任务执行结束-------------------");
	}
}

数据库表 schedule_job

执行效果

动态部分

动态部分就是在静态部分的基础上,加入controller,直接通过接口来实时操作定时任务

package com.zzg.controller;

import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.zzg.common.Result;
import com.zzg.entity.ScheduleJob;
import com.zzg.enums.JobOperateEnum;
import com.zzg.service.IQuartzService;
import com.zzg.service.ScheduleJobService;

import io.swagger.annotations.ApiOperation;

@RestController
@RequestMapping("/api/quartz")
public class QuartzController {
	@Autowired
	private ScheduleJobService scheduleJobService;
	@Autowired
	private IQuartzService quartzService;
	
	@PostMapping("/startAllJob")
	@ApiOperation("开始所有定时任务")
	public Result startAllJob() throws SchedulerException {
		quartzService.startAllJob();
	    return Result.ok();
	}

	@PostMapping("/pauseAllJob")
	@ApiOperation("暂停所有定时任务")
	public Result pauseAllJob() throws SchedulerException {
		quartzService.pauseAllJob();
	    return Result.ok();
	}
	
	@PostMapping("/startJob")
	@ApiOperation("启动定时任务")
	public Result start(@RequestBody ScheduleJob form) {
		
		try {
			quartzService.operateJob(JobOperateEnum.START,form);
			// 补充业务逻辑
			ScheduleJob entity = scheduleJobService.getById(form.getId());
			entity.setStatus(0);
			scheduleJobService.saveOrUpdate(entity);
		} catch (SchedulerException e) {
			// 堆栈异常输出
			e.printStackTrace();
			return Result.error("启动定时任务失败");
		}
	    return Result.ok();
	}

	@PostMapping("/pauseJob")
	@ApiOperation("暂停定时任务")
	public Result pause(@RequestBody ScheduleJob form) {
		try {
			quartzService.operateJob(JobOperateEnum.PAUSE, form);
			
			// 补充业务逻辑
			ScheduleJob entity = scheduleJobService.getById(form.getId());
			entity.setStatus(1);
			scheduleJobService.saveOrUpdate(entity);
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return Result.error("暂停定时任务失败");
		}
	    return Result.ok();
	}

	@PostMapping("/deleteJob")
	@ApiOperation("删除定时任务")
	public Result delete(@RequestBody ScheduleJob form) {
		try {
			quartzService.operateJob(JobOperateEnum.DELETE, form);
			
			// 补充其他业务逻辑
			scheduleJobService.removeById(form.getId());
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return Result.error("删除定时任务失败");
		}
	    return Result.ok();
	}
}

 

 知识拓展:CronTrigger配置完整格式

[秒] [分] [小时] [日] [月] [周] [年]

参数

  1. 秒(0~59)
  2. 分钟(0~59)
  3. 小时(0~23)
  4. 天(0~31,但是你需要考虑你月的天数)
  5. 月(0~11)
  6. 周(1~7)
  7. 年份(1970-2099)(可省略)

eg:

  1. 0 0 0/1 * * ? // 每小时执行一次
  2. 0 0 12 * * ? // 每天12点触发
  3. 0 0 22 L * ? // 每月最后一天的22点触发

 源码下载地址:

百度网盘下载地址及其提取码(包含项目源码+项目初始化脚本):

链接:https://pan.baidu.com/s/19rqjrBvRX6PNbcSuyTU5BQ 
提取码:1234 
 

 

以上是关于Spring Boot2.x + Quartz 定时任务模块的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot2.X 配置文件自动解密读取

spring boot2.x和activiti如何配置

Spring Boot2.X中findOne的用法

Spring Boot2.x 的Druid连接池配置[附带监控]

spring cloudspring boot2.x下 使用feign,注解@EnableFeignClients 找不到的解决方法

最全Spring Boot2.x系列Config配置集成篇-1参数配置