目 录CONTENT

文章目录

SpringBoot项目集成QuartzJob任务

芈亓
2023-09-17 / 0 评论 / 2 点赞 / 283 阅读 / 3,170 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-09-26,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

场景描述

在项目的实际场景中,我们经常会遇到一些任务需要每天、每周、或者固定时间去执行,所以在项目中加入Quartz框架,来更好的对这些事情做管理,只需要配置任务对应的CORN表达式,添加到任务里面即可让他自动化的实现对任务的管理。

集成教程

1. 项目POM文件中引入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

2. 在项目application.properties中新增如下配置

注意:
1、如果需要quartz 第一次运行时自动生成 quartz 所需的表那么 quartzJob? 后面的配置为 :allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
待第一次运行后可以再根据自己的需要修改
2、配置文件中的 initialize-schema: always 配置的 always 属性意思是,每次初始化都会重新生成表(执行一次删除,执行一次创建),生成后,可以修改为 never
只有以上两个条件同时配置满足,才能使quartz 在第一次运行时,自动生成所需的表


#  quartz定时任务,采用数据库方式  如果需要quartz 第一次运行时自动生成 quartz 所需的表那么 quartzJob? 后面的配置为 :allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.quartz.job-store-type=jdbc
# ?配置文件中的 initialize-schema: always 配置的 always 属性意思是,每次初始化都会重新生成表(执行一次删除,执行一次创建),生成后,可以修改为 never
spring.quartz.jdbc.initialize-schema=never


# 时任务启动开关,true-开  false-关
spring.quartz.auto-startup=true
#??1???????
spring.quartz.startup-delay=1s

spring.quartz.overwrite-existing-jobs=true
# Quartz Scheduler Properties
spring.quartz.properties.org.quartz.scheduler.instanceName = MyScheduler
spring.quartz.properties.org.quartz.scheduler.instanceId = AUTO

spring.quartz.properties.org.quartz.jobStore.class = org.springframework.scheduling.quartz.LocalDataSourceJobStore
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# ??????
spring.quartz.properties.org.quartz.jobStore.tablePrefix = QRTZ_
spring.quartz.properties.org.quartz.jobStore.isClustered = true
spring.quartz.properties.org.quartz.jobStore.misfireThreshold = 12000
spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval = 15000

# ????????
spring.quartz.properties.org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
spring.quartz.properties.org.quartz.threadPool.threadCount = 1
spring.quartz.properties.org.quartz.threadPool.threadPriority = 5
spring.quartz.properties.org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

上面配置好之后,启动项目会直接在对应链接的数据库下生成11张默认的表,均是以QRTZ开头,如下图

数据表图

3. 在生成的表上我们还需要新增一张自己添加任务的配置表,具体如下

CREATE TABLE `sys_quartz_job` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `del_flag` int(11) DEFAULT NULL COMMENT '删除状态',
  `update_by` varchar(32) DEFAULT NULL COMMENT '修改人',
  `update_time` datetime DEFAULT NULL COMMENT '修改时间',
  `job_class_name` varchar(255) DEFAULT NULL COMMENT '任务类名',
  `cron_expression` varchar(255) DEFAULT NULL COMMENT 'cron表达式',
  `parameter` varchar(255) DEFAULT NULL COMMENT '参数',
  `meeting_record_id` int(11) DEFAULT NULL COMMENT '会议室记录id',
  `description` varchar(255) DEFAULT NULL COMMENT '描述',
  `status` int(11) DEFAULT NULL COMMENT '状态 0正常 -1停止',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

4. 添加代码,补充Quartz的功能

此处功能是可以实现对定时任务的管理,比如添加、删除、重新配置、立即执行定时任务等。

Controller类

// Controller类

/**
 * @Description: 定时任务
 */
@RestController
@RequestMapping("/sys/quartzJob")
@Slf4j
@Api(tags = "定时任务接口")
public class QuartzJobController {
    @Autowired
    private QuartzJobService quartzJobService;
    @Autowired
    private Scheduler scheduler;

    /**
     * 分页列表查询
     *
     * @param quartzJob
     * @param page
     * @param pageSize
     * @return
     */
    @ApiOperation(value = "分页列表查询", notes = "分页列表查询")
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public ApiResult queryPageList(QuartzJob quartzJob, @RequestParam(value = "page", defaultValue = "1") int page,
                                   @RequestParam(value = "pageSize", defaultValue = "20") int pageSize) {
        Page<QuartzJob> list = quartzJobService.selectList(quartzJob,page,pageSize);
        ApiResult apiResult = new ApiResult();
        if (CollectionUtils.isNotEmpty(list.getItems())){
            apiResult.setData(list);
        }
        return ApiResult.ok("list",list);

    }

    /**
     * 添加定时任务
     *
     * @param quartzJob
     * @return
     */
    //@RequiresRoles("admin")
    @ApiOperation(value = "添加定时任务", notes = "添加定时任务")
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public ApiResult add(@RequestBody QuartzJob quartzJob) {
        boolean b = quartzJobService.saveAndScheduleJob(quartzJob);
        if (b == true) {
            return ApiResult.ok("add");
        }
        return ApiResult.fail("add","添加定时任务失败");
    }

    /**
     * 更新定时任务
     *
     * @param quartzJob
     * @return
     */
    //@RequiresRoles("admin")
    @ApiOperation(value = "更新定时任务", notes = "更新定时任务")
    @RequestMapping(value = "/edit", method ={RequestMethod.PUT, RequestMethod.POST})
    public ApiResult eidt(@RequestBody QuartzJob quartzJob) {
        try {
            quartzJobService.editAndScheduleJob(quartzJob);
        } catch (SchedulerException e) {
            log.error(e.getMessage(),e);
            return ApiResult.fail("edit","更新定时任务失败!");
        }
        return ApiResult.ok("edit");
    }

    /**
     * 通过id删除
     *
     * @param id
     * @return
     */
    @ApiOperation(value = "通过id删除", notes = "通过id删除")
    @RequestMapping(value = "/delete", method = RequestMethod.DELETE)
    public ApiResult delete(@RequestParam(name = "id", required = true) String id) {
        QuartzJob quartzJob = quartzJobService.getById(Long.valueOf(id));
        if (quartzJob == null) {
            return ApiResult.fail("delete","未找到对应实体");
        }
        quartzJobService.deleteAndStopJob(Long.valueOf(id));
        return ApiResult.ok("delete");

    }

    /**
     * 批量删除
     *
     * @param ids
     * @return
     */
    @ApiOperation(value = "批量删除", notes = "批量删除")
    @RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE)
    public ApiResult deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
        if (ids == null || "".equals(ids.trim())) {
            return ApiResult.fail("deleteBatch","参数不识别!");
        }
        for (String id : Arrays.asList(ids.split(","))) {
            QuartzJob job = quartzJobService.getById(Long.valueOf(id));
            quartzJobService.deleteAndStopJob(Long.valueOf(id));
        }
        return ApiResult.ok("deleteBatch");
    }

    /**
     * 暂停定时任务
     *
     * @param id
     * @return
     */
    @GetMapping(value = "/pause")
    @ApiOperation(value = "停止定时任务")
    public ApiResult pauseJob(@RequestParam(name = "id") String id) {
        QuartzJob job = quartzJobService.getById(Long.valueOf(id));
        if (job == null) {
            return ApiResult.fail("pause","定时任务不存在!");
        }
        quartzJobService.pause(job);
        return ApiResult.ok("pause");
    }

    /**
     * 启动定时任务
     *
     * @param id
     * @return
     */
    @GetMapping(value = "/resume")
    @ApiOperation(value = "启动定时任务")
    public ApiResult resumeJob(@RequestParam(name = "id") String id) {
        QuartzJob job = quartzJobService.getById(Long.valueOf(id));
        if (job == null) {
            return ApiResult.fail("resume","定时任务不存在!");
        }
        quartzJobService.resumeJob(job);
        //scheduler.resumeJob(JobKey.jobKey(job.getJobClassName().trim()));
        return ApiResult.ok("resume");
    }

    /**
     * 通过id查询
     *
     * @param id
     * @return
     */
    @ApiOperation(value = "通过id查询", notes = "通过id查询")
    @RequestMapping(value = "/queryById", method = RequestMethod.GET)
    public ApiResult queryById(@RequestParam(name = "id", required = true) String id) {
        QuartzJob quartzJob = quartzJobService.getById(Long.valueOf(id));
        ApiResult apiResult = new ApiResult();
        apiResult.setData(quartzJob);
        return apiResult;
    }

    /**
     * 立即执行
     * @param id
     * @return
     */
    @ApiOperation(value = "立即执行", notes = "立即执行")
    @GetMapping("/execute")
    public ApiResult execute(@RequestParam(name = "id", required = true) String id) {
        QuartzJob quartzJob = quartzJobService.getById(Long.valueOf(id));
        if (quartzJob == null) {
            return ApiResult.fail("execute","未找到对应实体");
        }
        try {
            quartzJobService.execute(quartzJob);
        } catch (Exception e) {
            //e.printStackTrace();
            log.info("定时任务 立即执行失败>>"+e.getMessage());
            return ApiResult.fail("execute","执行失败!");
        }
        return ApiResult.ok("execute");
    }

}


Service类

@Service
@Slf4j
public class QuartzJobService{

    @Resource
    private QuartzJobMapper quartzJobMapper;
    @Autowired
    private Scheduler scheduler;

    /**
     * 立即执行的任务分组
     */
    private static final String JOB_TEST_GROUP = "test_group";

    public Page<QuartzJob> selectList(QuartzJob quartzJob, int page, int pageSize) {
        Page<QuartzJob> pageQuartzJobVo = Page.create(page, pageSize);
        Example example = new Example(QuartzJob.class);
        Example.Criteria criteria = example.createCriteria();
        if (!StringUtils.isEmpty(quartzJob.getJobClassName())) {
            criteria.andLike("jobClassName","%"+ quartzJob.getJobClassName() +"%");
        }
        if (!StringUtils.isEmpty(quartzJob.getCronExpression())) {
            criteria.andEqualTo("cronExpression", quartzJob.getCronExpression());
        }
        if (!StringUtils.isEmpty(quartzJob.getDescription())) {
            criteria.andLike("", "%"+quartzJob.getDescription() +"%");
        }
        // 查询未删除的定时列表
        criteria.andEqualTo("delFlag",CommonConstant.DEL_FLAG_0.getCode());
        List<QuartzJob> quartzJobs = quartzJobMapper.selectByExample(example);
        pageQuartzJobVo.setItems(quartzJobs);
        pageQuartzJobVo.setTotalCount(quartzJobs.size());
        return pageQuartzJobVo;
    }

    public boolean saveAndScheduleJob(QuartzJob quartzJob) {
       // DB设置修改
       quartzJob.setDelFlag(CommonConstant.DEL_FLAG_0.getCode());
        boolean success = this.save(quartzJob);
        if (success) {
            if (CommonConstant.STATUS_NORMAL.getCode().equals(quartzJob.getStatus())) {
                // 定时器添加
                this.schedulerAdd(String.valueOf(quartzJob.getId()), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
            }
        }
        return success;
    }

    private boolean save(QuartzJob quartzJob) {
        if (StringUtils.isEmpty(quartzJob.getCreateBy())){
            quartzJob.setCreateBy("admin");
        }
        if (StringUtils.isEmpty(quartzJob.getCreateTime())){
            quartzJob.setCreateTime(new Date());
        }
        int i = quartzJobMapper.insertSelective(quartzJob);
        if (i == 1){
            return true;
        }
        return false;
    }

    public void editAndScheduleJob(QuartzJob quartzJob) throws SchedulerException {
        if (CommonConstant.STATUS_NORMAL.equals(quartzJob.getStatus())) {
            schedulerDelete(quartzJob.getId());
            schedulerAdd(String.valueOf(quartzJob.getId()), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
        }else{
            scheduler.pauseJob(JobKey.jobKey(String.valueOf(quartzJob.getId())));
        }
        this.updateById(quartzJob);
    }

    private void updateById(QuartzJob quartzJob) {
        QuartzJob q = new QuartzJob();
        q.setId(quartzJob.getId());
        if (!StringUtils.isEmpty(quartzJob.getCronExpression())){
            q.setCronExpression(quartzJob.getCronExpression());
        }
        if (!StringUtils.isEmpty(quartzJob.getJobClassName())){
            q.setJobClassName(quartzJob.getJobClassName());
        }
        if (!StringUtils.isEmpty(quartzJob.getDescription())){
            q.setDescription(quartzJob.getDescription());
        }
        if (!StringUtils.isEmpty(quartzJob.getDelFlag())){
            q.setDelFlag(quartzJob.getDelFlag());
        }
        if (!StringUtils.isEmpty(quartzJob.getStatus())){
            q.setStatus(quartzJob.getStatus());
        }
        if (!StringUtils.isEmpty(quartzJob.getParameter())){
            q.setParameter(quartzJob.getParameter());
        }
        q.setUpdateBy("admin");
        q.setUpdateTime(new Date());
        quartzJobMapper.updateByPrimaryKeySelective(q);
    }

    /**
     * 添加定时任务
     *
     * @param jobClassName
     * @param cronExpression
     * @param parameter
     */
    private void schedulerAdd(String id, String jobClassName, String cronExpression, String parameter) {
        try {
            // 启动调度器
            scheduler.start();

            // 构建job信息
            JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(id).usingJobData("parameter", parameter).build();

            // 表达式调度构建器(即任务执行的时间)
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

            // 按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(id).withSchedule(scheduleBuilder).build();

            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException e) {
            throw new BusinessException("创建定时任务失败" + e);
        } catch (RuntimeException e) {
            throw new BusinessException(e.getMessage() + e);
        }catch (Exception e) {
            throw new BusinessException("后台找不到该类名:"  + jobClassName  + e);
        }
    }

    /**
     * (删除&停止)删除定时任务
     */
    public boolean deleteAndStopJob(Long id) {
        schedulerDelete(id);
        QuartzJob quartzJob = new QuartzJob();
        quartzJob.setId(id);
        quartzJob.setDelFlag(CommonConstant.DEL_FLAG_1.getCode());
        quartzJob.setStatus(CommonConstant.STATUS_UNNORMAL.getCode());
        int i = quartzJobMapper.updateByPrimaryKeySelective(quartzJob);
        if (i == 1){
            return true;
        }
        return false;
    }


    /**
     * 删除定时任务
     *
     * @param id
     */
    private void schedulerDelete(Long id) {
        try {
            scheduler.pauseTrigger(TriggerKey.triggerKey(String.valueOf(id)));
            scheduler.unscheduleJob(TriggerKey.triggerKey(String.valueOf(id)));
            scheduler.deleteJob(JobKey.jobKey(String.valueOf(id)));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BusinessException("删除定时任务失败");
        }
    }

    private static Job getClass(String classname) throws Exception {
        Class<?> class1 = Class.forName(classname);
        return (Job) class1.newInstance();
    }


    public QuartzJob getById(Long id) {
        QuartzJob quartzJob = new QuartzJob();
        quartzJob.setId(id);
        quartzJob.setStatus(CommonConstant.STATUS_NORMAL.getCode());
        QuartzJob quartzJob1 = quartzJobMapper.selectOne(quartzJob);
        return quartzJob1;
    }


    public void pause(QuartzJob quartzJob){
        schedulerDelete(quartzJob.getId());
        quartzJob.setStatus(CommonConstant.STATUS_UNNORMAL.getCode());
        this.updateById(quartzJob);
    }

    public void resumeJob(QuartzJob quartzJob) {
        schedulerDelete(quartzJob.getId());
        schedulerAdd(String.valueOf(quartzJob.getId()), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
        quartzJob.setStatus(CommonConstant.STATUS_NORMAL.getCode());
        this.updateById(quartzJob);
    }

    public void execute(QuartzJob quartzJob) throws Exception {
        String jobName = quartzJob.getJobClassName().trim();
        Date startDate = new Date();
        String ymd = DateUtils.format(startDate,"yyyymmddhhmmss");
        String identity =  jobName + ymd;
        //3秒后执行 只执行一次
        // update-begin--author:sunjianlei ---- date:20210511--- for:定时任务立即执行,延迟3秒改成0.1秒-------
        startDate.setTime(startDate.getTime() + 100L);
        // update-end--author:sunjianlei ---- date:20210511--- for:定时任务立即执行,延迟3秒改成0.1秒-------
        // 定义一个Trigger
        SimpleTrigger trigger = (SimpleTrigger)TriggerBuilder.newTrigger()
                .withIdentity(identity, JOB_TEST_GROUP)
                .startAt(startDate)
                .build();
        // 构建job信息
        JobDetail jobDetail = JobBuilder.newJob(getClass(jobName).getClass()).withIdentity(identity).usingJobData("parameter", quartzJob.getParameter()).build();
        // 将trigger和 jobDetail 加入这个调度
        scheduler.scheduleJob(jobDetail, trigger);
        // 启动scheduler
        scheduler.start();
    }
}


Mapper类


public interface QuartzJobMapper extends Mapper<QuartzJob> {

    /**
     * 根据jobClassName查询
     * @param jobClassName 任务类名
     * @return
     */
    public List<QuartzJob> findByJobClassName(@Param("jobClassName") String jobClassName);
}


实体类


@Data
@Table(name = "sys_quartz_job")
public class QuartzJob implements Serializable {
    /**
     * id
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * 创建人
     */
    @Column(name = "create_by")
    private String createBy;

    /**
     * 创建时间
     */
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Column(name = "create_time")
    private Date createTime;

    /**
     * 删除状态
     */
    @Column(name = "del_flag")
    private Integer delFlag;

    /**
     * 修改人
     */
    @Column(name = "update_by")
    private String updateBy;

    /**
     * 修改时间
     */
    @Column(name = "update_time")
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;

    /**
     * 任务类名
     */
    @Column(name = "job_class_name")
    private String jobClassName;

    /**
     * cron表达式
     */
    @Column(name = "cron_expression")
    private String cronExpression;

    /**
     * 参数
     */
    private String parameter;


    /**
     * 描述
     */
    private String description;

    /**
     * 状态 0正常 -1停止
     */
    private Integer status;
}


5. 简单的Job任务类,这个根据自己的实际需求进行更改

根据实际开发的需要,选择适合自己的任务类搭配即可实现自己想要的效果

任务类一


/**
 * @Description: 同步定时任务测试
 *
 * 此处的同步是指 当定时任务的执行时间大于任务的时间间隔时
 * 会等待第一个任务执行完成才会走第二个任务
 */
@PersistJobDataAfterExecution // 持久化JobDataMap里的数据,使下一个定时任务还能获取到这些值
@DisallowConcurrentExecution // 禁止并发多任务执行,所以永远只有一个任务在执行中
@Slf4j
public class Test1Job implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info(" --- 同步任务调度开始 --- " + " Job Execution key:"+jobExecutionContext.getJobDetail().getKey());
        try {
            //此处模拟任务执行时间 5秒  任务表达式配置为每秒执行一次:0/1 * * * * ? *
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //测试发现 每5秒执行一次
        log.info(" --- 执行完毕,时间:"+new Date()+"---"  + " 线程名"+ Thread.currentThread().getName() );
    }
}


任务类二


@Slf4j
public class Test2Job implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info(" Job Execution key:"+jobExecutionContext.getJobDetail().getKey());
        log.info(String.format(" rih-health-center 普通定时任务 Test2Job !  时间:" + new Date()));
    }
}


任务类三


@Slf4j
public class Test3Job implements Job {

    /**
     * 若参数变量名修改 QuartzJobController中也需对应修改
     */
    private String parameter;

    public void setParameter(String parameter) {
        this.parameter = parameter;
    }
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        log.info(" Job Execution key:"+jobExecutionContext.getJobDetail().getKey());
        log.info( String.format("welcome %s! 带参数定时任务 Test3Job !   时间:" + new Date(), this.parameter));
    }
}


至此,Quartz Job集成完成

2

评论区