SpringBoot整合Qaurtz系统设计与实现思路

一、前言

后端基于基于SpringBoot框架进行设计。在系统中可以设计定时任务、定时计划两大模块。

定时任务:定义的一些定时的任务。
任务的名称、任务描述、类名或者URL、状态(是否关联)、是否是系统计划、修改人、修改时间等。

定时计划:是真正执行的任务。
(1)每个定时计划可以设定一条或者多条定时任务(触发任务),并进行发布。
(2)需要设置定时计划的触发器(按天、周、月等)。

二、数据库表设计

相关的数据库表设计4个表,分别是定时任务表、定时计划表、定时任务定时计划关联表、定时计划执行记录表。

1、cfg_scheduletask(定时任务表)

字段解释
IID主键
ISYSTEM系统定义[0:系统级 (系统级不允许删除) 1:自定义级(可以进行删除操作)
SNAME任务名称
SCLASSNAME存储任务的类名或者URL地址
SDESCRIPTION任务描述
ISTATUS状态[0:发布 1:未发布](定时计划中发布后,此状态为0)
IMODIFIED修改人
DMODIFIED修改时间
IFLAG标志[0:启用 1:禁用](此任务是否启用,启用的话才可以被定时计划中选择为触发任务)

2、cfg_scheduleplan(定时计划表)

除了主键、名称、描述、修改人、修改时间、状态[0:发布 1:未发布]、标志[0:启用 1:禁用]字段以外,还包括的关键字段有:

字段解释
TSTARTTIME触发开始时间
TENDTIME触发结束时间
ITRIGGERMODE触发方式[1:一次 2:按天 3:按周 4:按月]
ITRIGGERMODE重复间隔
IDAYVALUE按天间隔
SDAYSOFWEEK按周间隔[3,5 每周三和周五]
SMONTHS月重复
IMONTHTYPE月中日重复方式[1:月中的日 2:月末 3:月中的星期]
SDAYSOFMONTH月中日重复
IMONTHWEEK月中周重复
IMONTHWEEKDAY月中周重复方式

3、cfg_scheduleplantask(定时计划与定时任务关联表)

字段解释
IID主键
IPLANID计划ID
ITASKID任务ID

4、cfg_scheduleplan_log(定时任务执行记录表)
记录每一条定时计划中的定时任务的执行情况。

字段解释
IID主键
IPLANID定时计划ID
IPLANTASKID定时任务ID
SCONTENT任务执行结果描述
ISUCCESS执行结果,0:成功,1:失败
TSTARTTIME任务执行开始时间
TENDTIME任务执行结束时间

三、界面设计

可以有两个界面。一个界面对定时任务进行设置。一个界面制定定时计划以及定时计划的发布、关闭、删除等操作。

四、代码设计逻辑

1、每个定时任务需要执行的方法直接写成接口的形式。这样既可以方便前端调用方法,而且不需要写一堆JOB,只需要写一个公共的JOB然后通过传参不同调用不同的接口URL就可以了。

2、用户只需要点击定时计划的发布按钮,直接调用公共方法初始化调度器、查询需要执行的任务。其中JOB传值为一个公共的JOB接口,在里面可以通过LOG日志记录定时任务的执行情况,同时调用一个公共方法:访问URL调用接口方法。

3、所以说就是把所有的JOB集结成一个调用接口的方法,并记录定时计划的执行情况。

五、SpringBoot整合Quartz实现以上功能小案例

5.1 代码设计

首先,项目结构:

在这里插入图片描述

1、编写定时任务接口代码,定义两个定时任务接口restA和restB。

package com.mytest.testQuartz.quartz.rest;

import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zll
 * @version 1.0
 * @date 2020/7/28 13:27
 */
@RestController
@RequestMapping("/testApi")
public class restA {

    @PostMapping("/task01")
    public String testGet() {
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        System.out.println(createTime + "     task01执行一次!");

        return "task01======123456";
    }


}

package com.mytest.testQuartz.quartz.rest;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @author zll
 * @version 1.0
 * @date 2020/7/28 13:27
 */
@RestController
@RequestMapping("/testApi")
public class restB {

    @PostMapping("/task02")
    public String testGet() {
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        System.out.println(createTime + "     task01执行一次!");

        return "task02======ABCDEF";
    }

}

2、定义公共类quartzManager,定义启动定时计划的方法,传参可以是数据库中存的定时计划的id等,只要可以确定定时任务的具体信息。

package com.mytest.testQuartz.quartz;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

/**
 * @author zll
 * @version 1.0
 * @date 2020/7/28 13:26
 */
public class quartzManager {


    private static SchedulerFactory schedulerFactory = new StdSchedulerFactory();

    /**
     * 启动定时任务的方法
     * @param url
     * @param cron
     * @param id
     */
    public static void test(String url,String cron, String id) {
        //String thistype = "2";//默认按照秒进行,方便测试
        Class name = null;// Job类名
        // 1、创建调度器Scheduler
        Scheduler scheduler = null;
        try {
            scheduler = schedulerFactory.getScheduler();
            // 2、创建JobDetail实例,并与PrintWordsJob类绑定(Job执行内容)
            name = Class.forName("com.mytest.testQuartz.quartz.job.quartzRest");
            JobDetail jobDetail1 = JobBuilder.newJob(name)
                    .withIdentity( id+"job1",   id+"group1").build();
            jobDetail1.getJobDataMap().put("url",url);
            // 3、构建Trigger实例,每隔1s执行一次
            CronTrigger cronTrigger1 = TriggerBuilder.newTrigger().withIdentity( id+"trigger1",  id+"triggerGroup1")
                    .usingJobData("trigger1", "这是testJob1的trigger")
                    .startNow()//立即生效
                    .withSchedule(CronScheduleBuilder.cronSchedule(cron))
                    .build();

            //4、执行
            scheduler.scheduleJob(jobDetail1, cronTrigger1);
            System.err.println("--------" + "   scheduler start ! " + cron + "------------");
            scheduler.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭某定时任务的方法
     */


}


3、定义公共JOB类quartzRest ,所有的定时任务都执行这个job,这个job会根据url地址去调用任务的接口方法。

package com.mytest.testQuartz.quartz.job;

import com.mytest.testQuartz.quartz.service.InterfaceService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.util.HashMap;
import java.util.Map;

/**
 * @author zll
 * @version 1.0
 * @date 2020/7/28 14:03
 */
public class quartzRest implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        String url = context.getJobDetail().getJobDataMap().getString("url");
        Map<String, Object> map = new HashMap<>();
        //执行的JOB任务就是:调用自己的接口方法!
        new InterfaceService().sendData(url, map);
    }
}


4、定义公共类InterfaceService,实现调用URL接口的功能。即可以完成对任务接口的调用。

package com.mytest.testQuartz.quartz.service;

import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.Map;

/**
 * @author zll
 * @version 1.0
 * @date 2020/7/28 14:21
 */
public class InterfaceService {
    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    RestTemplate restTemplate;

    public String sendData(String url, Map<String, Object> mp) {
        restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        JSONObject oparam = new JSONObject(mp);
        //log.debug(url + " : " + oparam.toJSONString());
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        map.setAll(mp);
        String response = "";
        try {
            HttpEntity<String> httpEntity = new HttpEntity<>(oparam.toString(), headers);
            ResponseEntity<String> response2 = restTemplate.postForEntity(url, httpEntity, String.class);
            response = response2.getBody();
            //log.debug(url+"的返回值为:=========="+response);
            System.out.println(url+"的返回值为:=========="+response);
        } catch (Exception e) {
            response = "接口[" + url + "]调用异常!" + e.getMessage();
          //  log.error(response);
        }
        return response;
    }
}

5.2 来测试吧

编写一个测试类myTest。我们测试的时候需要启动整个SpringBoot项目,然后再启动这个测试类来访问本地接口。我的项目端口号是8080。

1、测试类代码(其中定义的参数都可以是前端传的各种参数,真的做系统的话可以直接传定时计划id然后再查需要执行的任务以及详细信息)

package com.mytest.testQuartz.quartz;

/**
 * @author zll
 * @version 1.0
 * @date 2020/7/28 14:04
 */
public class myTest {
    private static String urlA = "http://127.0.0.1:8080/testApi/task01";
    private static String urlB = "http://127.0.0.1:8080/testApi/task02";
    private static String cronA = "*/10 * * * * ?";
    private static String cronB = "*/5 * * * * ?";

    public static void main(String args[]) {
        quartzManager.test(urlA, cronA, "1");
        quartzManager.test(urlB, cronB, "2");
    }
}

2、启动整个项目。
3、启动测试类myTest 。
4、结果:成功访问到本地的两个接口,并定时执行。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值