Spring Boot 动态定时任务---quartz(不需要重启服务)

一、配置文件quartz.properties

#ID设置为自动获取 每一个必须不同 (所有调度器实例中是唯一的)
org.quartz.scheduler.instanceId=AUTO
#指定调度程序的主线程是否应该是守护线程
org.quartz.scheduler.makeSchedulerThreadDaemon=true
#ThreadPool实现的类名
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
#ThreadPool配置线程守护进程
org.quartz.threadPool.makeThreadsDaemons=true
#线程数量
org.quartz.threadPool.threadCount=20
#线程优先级
org.quartz.threadPool.threadPriority=5
#数据保存方式为持久化
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
#StdJDBCDelegate说明支持集群
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#quartz内部表的前缀
org.quartz.jobStore.tablePrefix=QRTZ_
#是否加入集群
org.quartz.jobStore.isClustered=true
#容许的最大作业延长时间
org.quartz.jobStore.misfireThreshold=55000

说明:在使用quartz做持久化的时候需要用到quartz的11张表,可以去quartz官网下载对应版本的quartz,解压打开docs/dbTables里面有对应数据库的建表语句。关于quartz.properties配置的详细解释可以查看quartz官网。另外新建一张表用于存放定时任务基本信息和描述等信息,定时任务的增、删、改、执行等功能与此表没有任何关系。

二、定时任务基本信息表

三、Quartz的核心配置类

package com.demo.demo.config;

import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;

/*
 * @Author jt
 * @Description //Quartz的核心配置类
 * @Date 2020/8/11
 **/

@Configuration
public class ConfigureQuartz {

    //配置JobFactory
    @Bean
    public JobFactory jobFactory(ApplicationContext applicationContext) {
        AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
        jobFactory.setApplicationContext(applicationContext);
        return jobFactory;
    }

    /**
     * SchedulerFactoryBean这个类的真正作用提供了对org.quartz.Scheduler的创建与配置,并且会管理它的生命周期与Spring同步。
     * org.quartz.Scheduler: 调度器。所有的调度都是由它控制。
     *
     * @param dataSource 为SchedulerFactory配置数据源
     * @param jobFactory 为SchedulerFactory配置JobFactory
     */
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource, JobFactory jobFactory) throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        //可选,QuartzScheduler启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录
        factory.setOverwriteExistingJobs(true);
        factory.setAutoStartup(true); //设置自行启动
        factory.setDataSource(dataSource);
        factory.setJobFactory(jobFactory);
        factory.setQuartzProperties(quartzProperties());
        return factory;
    }

    //从quartz.properties文件中读取Quartz配置属性
    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }

    //配置JobFactory,为quartz作业添加自动连接支持
    public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
        private transient AutowireCapableBeanFactory beanFactory;

        @Override
        public void setApplicationContext(final ApplicationContext context) {
            beanFactory = context.getAutowireCapableBeanFactory();
        }

        @Override
        protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
            final Object job = super.createJobInstance(bundle);
            beanFactory.autowireBean(job);
            return job;
        }
    }
}

四、Ctrl层

package com.demo.demo.ctrl;

import com.demo.demo.entity.JobEntity;
import com.demo.demo.service.DynamicJobService;
import org.quartz.*;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

/*
 * @Author jt
 * @Description //任务调度
 * @Date 2020/8/11
 **/
@RestController
@RequestMapping("job")
public class JobController {

    @Resource
    private SchedulerFactoryBean factory;
    @Resource
    private DynamicJobService jobService;

    //被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次
    //初始化启动所有的Job
    @PostConstruct
    public void initialize() {
        jobService.reStartAllJobs();
    }

    //根据ID重启某个Job
    @GetMapping("/refresh/{id}")
    public String refresh(@PathVariable Integer id) {
        String result;
        JobEntity entity = jobService.getJobEntityById(id);
        if (entity == null) return "error: id is not exist ";
        TriggerKey triggerKey = new TriggerKey(entity.getName(), entity.getGroup());
        JobKey jobKey = jobService.getJobKey(entity);
        Scheduler scheduler = factory.getScheduler();
        try {
            scheduler.unscheduleJob(triggerKey);
            scheduler.deleteJob(jobKey);
            JobDataMap map = jobService.getJobDataMap(entity);
            JobDetail jobDetail = jobService.geJobDetail(jobKey, entity.getDescription(), map);
            if (entity.getStatus().equals("OPEN")) {
                scheduler.scheduleJob(jobDetail, jobService.getTrigger(entity));
                result = "Refresh Job : " + entity.getName() + "\t method: " + entity.getClassName() + "." + entity.getMethodName() + " success !";
                jobService.updateJobEntityWorkStatusByKey(entity);
            } else {
                result = "Refresh Job : " + entity.getName() + "\t method: " + entity.getClassName() + "." + entity.getMethodName() + " failed ! , " + "Because the Job status is " + entity.getStatus();
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
            result = "Error while Refresh " + e.getMessage();
        }
        return result;
    }

    //重启数据库中所有的Job
    @GetMapping("/refresh/all")
    public String refreshAll() {
        String result;
        try {
            jobService.reStartAllJobs();
            result = "SUCCESS";
        } catch (Exception e) {
            result = "EXCEPTION : " + e.getMessage();
        }
        return "refresh all jobs : " + result;
    }
}

五、biz层

package com.demo.demo.service.Impl;

import com.demo.demo.entity.JobEntity;
import com.demo.demo.job.DynamicJob;
import com.demo.demo.mapper.JobEntityMapper;
import com.demo.demo.service.DynamicJobService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Set;


@Service
@Slf4j
public class DynamicJobServiceImpl implements DynamicJobService {


    @Resource
    private SchedulerFactoryBean factory;
    @Resource
    private JobEntityMapper jobEntityMapper;

    /**
     * 重新启动所有的job
     */
    public void reStartAllJobs() {
        try {
            Scheduler scheduler = factory.getScheduler();
            Set<JobKey> set = scheduler.getJobKeys(GroupMatcher.anyGroup());
            for (JobKey jobKey : set) {
                scheduler.deleteJob(jobKey);
            }
            for (JobEntity job : this.loadJobs()) {
                log.info("Job register name : {} , group : {} , cron : {}", job.getName(), job.getGroup(), job.getCron());
                JobDataMap map = this.getJobDataMap(job);
                JobKey jobKey = this.getJobKey(job);
                JobDetail jobDetail = this.geJobDetail(jobKey, job.getDescription(), map);
                if ("OPEN".equals(job.getStatus())) {
                    scheduler.scheduleJob(jobDetail, this.getTrigger(job));
                } else {
                    log.info("Job jump name : {} , Because {} status is {}", job.getName(), job.getName(), job.getStatus());
                }
                updateJobEntityWorkStatusByKey(job);
            }
            log.info("INIT SUCCESS");
        } catch (SchedulerException se) {
            log.info("INIT EXCEPTION {}", se.getMessage());
        }
    }

    //通过Id获取Job
    public JobEntity getJobEntityById(Integer id) {
        return jobEntityMapper.selectByPrimaryKey(id);
    }

    //更新Job的工作状态
    public void updateJobEntityWorkStatusByKey(JobEntity entity) {
        entity.setWorkStatus(1);
        jobEntityMapper.updateJobEntityWorkStatusByKey(entity.getId(), entity.getWorkStatus());
    }

    //从数据库中加载获取到所有Job
    private List<JobEntity> loadJobs() {
        return jobEntityMapper.selectAll();
    }

    //获取JobDataMap.(Job参数对象)
    public JobDataMap getJobDataMap(JobEntity job) {
        JobDataMap map = new JobDataMap();
        map.put("name", job.getName());
        map.put("group", job.getGroup());
        map.put("cronExpression", job.getCron());
        map.put("parameter", job.getParameter());
        map.put("JobDescription", job.getDescription());
        map.put("className", job.getClassName());
        map.put("methodName", job.getMethodName());
        map.put("params", job.getParams());
        map.put("status", job.getStatus());
        return map;
    }

    //获取JobDetail,JobDetail是任务的定义,而Job是任务的执行逻辑,JobDetail里会引用一个Job Class来定义
    public JobDetail geJobDetail(JobKey jobKey, String description, JobDataMap map) {
        return JobBuilder.newJob(DynamicJob.class)
                .withIdentity(jobKey)
                .withDescription(description)
                .setJobData(map)
                .storeDurably()
                .build();
    }

    //获取Trigger (Job的触发器,执行规则)
    public Trigger getTrigger(JobEntity job) {
        return TriggerBuilder.newTrigger()
                .withIdentity(job.getName(), job.getGroup())
                .withSchedule(CronScheduleBuilder.cronSchedule(job.getCron()))
                .build();
    }

    //获取JobKey,包含Name和Group
    public JobKey getJobKey(JobEntity job) {
        return JobKey.jobKey(job.getName(), job.getGroup());
    }
}

六、mapper层

package com.demo.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.demo.demo.entity.JobEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Component;

import java.util.List;

@Mapper
@Component
public interface JobEntityMapper extends BaseMapper<JobEntity> {

    @Select("select * from job_entity where id=#{id}")
    JobEntity selectByPrimaryKey(@Param("id") Integer id);

    @Update("update job_entity set work_status=#{workStatus} where id=#{id}")
    void updateJobEntityWorkStatusByKey(@Param("id") Integer id, @Param("workStatus") Integer workStatus);

    @Select("select * from job_entity")
    List<JobEntity> selectAll();
}

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值