Quartz实战:基于Quartz实现定时任务的动态调度,实现定时任务的增删改查

news2024/11/13 20:25:07

文章目录

  • 一、Quartz基础
  • 二、使用Quartz实现定时任务的动态调度
    • 1、使用Quartz-jobStore 持久化
    • 2、前端页面实现效果图
    • 3、自定义job表
    • 4、增删改查Controller
    • 5、Quartz工具类
    • 6、测试任务类
    • 7、springboot启动初始化定时任务
    • 8、自定义JobFactory,使Task注册为Bean
    • 9、省略的内容
    • 10、总结

一、Quartz基础

Quartz使用文档,使用Quartz实现动态任务,Spring集成Quartz,Quartz集群部署,Quartz源码分析

二、使用Quartz实现定时任务的动态调度

1、使用Quartz-jobStore 持久化

Quartz使用文档,使用Quartz实现动态任务,Spring集成Quartz,Quartz集群部署,Quartz源码分析

2、前端页面实现效果图

在这里插入图片描述

3、自定义job表

CREATE database quartz;

CREATE TABLE `sys_job` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `job_name` varchar(512) NOT NULL COMMENT '任务名称',
  `job_group` varchar(512) NOT NULL COMMENT '任务组名',
  `job_cron` varchar(512) NOT NULL COMMENT '时间表达式',
  `job_class_path` varchar(1024) NOT NULL COMMENT '类路径,全类型',
  `job_data_map` varchar(1024) DEFAULT NULL COMMENT '传递map参数',
  `job_status` int(2) NOT NULL COMMENT '状态:1启用 0停用',
  `job_describe` varchar(1024) DEFAULT NULL COMMENT '任务功能描述',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;

-- 插入3条数据,3个任务
-- 注意第三条,是一个发送邮件的任务,需要改成你自己的QQ和授权码。不知道什么是授权码的自己百度。
INSERT INTO `sys_job` (`id`, `job_name`, `job_group`, `job_cron`, `job_class_path`, `job_data_map`, `job_status`, `job_describe`) VALUES (22, 'test', 'test', '*/20 * * * * ?', 'com.demo.task.TestTask1', NULL, 1, 'a job a');
INSERT INTO `sys_job` (`id`, `job_name`, `job_group`, `job_cron`, `job_class_path`, `job_data_map`, `job_status`, `job_describe`) VALUES (23, 'test2', 'test', '*/30 * * * * ?', 'com.demo.task.TestTask2', NULL, 1, 'another job');
INSERT INTO `sys_job` (`id`, `job_name`, `job_group`, `job_cron`, `job_class_path`, `job_data_map`, `job_status`, `job_describe`) VALUES (24, 'test3', 'mail', '*/10 * * * * ?', 'com.demo.task.TestTask3', '{\"data\":{\"loginAccount\":\"改成你的QQ邮箱\",\"loginAuthCode\":\"改成你的邮箱授权码\",\"sender\":\"改成你的QQ邮箱\",\"emailContent\":\"    你好。\",\"emailContentType\":\"text/html;charset=utf-8\",\"emailSubject\":\"十万火急\",\"recipients\":\"改成你要的收件人邮箱,可以有多个,英文逗号隔开\"}}', 1, 'fdsafdfds');

-- 清空表的脚本
DELETE FROM QRTZ_BLOB_TRIGGERS;
DELETE FROM QRTZ_CALENDARS;
DELETE FROM QRTZ_CRON_TRIGGERS;
DELETE FROM QRTZ_FIRED_TRIGGERS;
DELETE FROM QRTZ_LOCKS;
DELETE FROM QRTZ_PAUSED_TRIGGER_GRPS;
DELETE FROM QRTZ_SCHEDULER_STATE;
DELETE FROM QRTZ_SIMPLE_TRIGGERS;
DELETE FROM QRTZ_SIMPROP_TRIGGERS;
DELETE FROM QRTZ_TRIGGERS;
DELETE FROM QRTZ_JOB_DETAILS;
COMMIT;

4、增删改查Controller

@Controller()
@RequestMapping("/job")
public class JobController {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	private ISysJobService sysJobService;

	/**
	 * 打开列表页
	 * @return
	 */
	@RequestMapping("/jobList")
	public String jobList() {
		return "jobListPage";
	}

	/**
	 * 打开详情界面
	 * @param id
	 * @param model
	 * @return
	 */
	@RequestMapping("/toDetail")
	public String toDetail(Integer id, Model model) {
		SysJob job = sysJobService.selectByPrimaryKey(id);
		model.addAttribute("job",job);
		return "jobDetail";
	}

	/**
	 * 打开修改界面
	 * @param id
	 * @param model
	 * @return
	 */
	@RequestMapping("/toUpdate")
	public String toUpdate(Integer id, Model model) {
		SysJob job = sysJobService.selectByPrimaryKey(id);
		model.addAttribute("job",job);
		return "jobUpdate";
	}

	/**
	 * 打开新增界面
	 * @return
	 */
	@RequestMapping("/toJob")
	public String toJob (){
		return "jobAdd";
	}

	/**
	 * 查询任务列表(带分页)
	 * 
	 * @return
	 */
	@RequestMapping(value = "/queryList", method = RequestMethod.GET)
	@ResponseBody
	public LayuiData queryJobList(HttpServletRequest request, HttpServletResponse response) {
		String idStr = request.getParameter("id");
		String jobName = request.getParameter("jobName");
		String jobGroup = request.getParameter("jobGroup");
		String jobCron = request.getParameter("jobCron");
		String jobClassPath = request.getParameter("jobClassPath");
		String jobDescribe = request.getParameter("jobDescribe");

		HashMap<String, String> map = new HashMap<String, String>();
		if (StringUtils.isNotBlank(idStr)) {
			map.put("id", idStr);
		}
		if (StringUtils.isNotBlank(jobName)) {
			map.put("jobName", jobName);
		}
		if (StringUtils.isNotBlank(jobGroup)) {
			map.put("jobGroup", jobGroup);
		}
		if (StringUtils.isNotBlank(jobCron)) {
			map.put("jobCron", jobCron);
		}
		if (StringUtils.isNotBlank(jobClassPath)) {
			map.put("jobClassPath", jobClassPath);
		}
		if (StringUtils.isNotBlank(jobDescribe)) {
			map.put("jobDescribe", jobDescribe);
		}

		int page = Integer.parseInt(request.getParameter("page"));
		int limit = Integer.parseInt(request.getParameter("limit"));
		if(page>=1){
			page = (page-1)*limit;
		}
		LayuiData layuiData = new LayuiData();

		try {
			List<SysJob> jobList = sysJobService.querySysJobList(map);
			int count = sysJobService.getJobCount();
			layuiData.setCode(0);
			layuiData.setCount(count);
			layuiData.setMsg("数据请求成功");
			layuiData.setData(jobList);
			return layuiData;
		} catch (Exception e) {
			throw new BizException("查询任务列表异常:" + e.getMessage());
		}
	}

	/**
	 * 添加定时任务
	 *
	 * @throws Exception
	 */
	@PostMapping(value = "/addJob")
	@ResponseBody
	@Transactional
	public int addjob(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logger.info("添加任务开始... ...");
		int num =0;
		String jobName = request.getParameter("jobName");
		String jobClassPath= request.getParameter("jobClassPath");
		String jobGroup= request.getParameter("jobGroup");
		String jobCron= request.getParameter("jobCron");
		String jobDescribe= request.getParameter("jobDescribe");
		String jobDataMap= request.getParameter("jobDataMap");
		
		if (StringUtils.isBlank(jobName)) {
			throw new BizException("任务名称不能为空");
		}
		if (StringUtils.isBlank(jobGroup)) {
			throw new BizException("任务组别不能为空");
		}
		if (StringUtils.isBlank(jobCron)) {
			throw new BizException("Cron表达式不能为空");
		}
		if (StringUtils.isBlank(jobClassPath)) {
			throw new BizException("任务类路径不能为空");
		}
		
		// 参数不为空时校验格式
		if(StringUtils.isNotBlank(jobDataMap)){
			try {
				JSONObject.parseObject(jobDataMap);
			} catch (Exception e) {
				throw new BizException("参数JSON格式错误");
			}
		}
		
		// 已存在校验
		SysJob queryBean = new SysJob();
		queryBean.setJobName(jobName);
		SysJob result = sysJobService.selectByBean(queryBean);
		if (null != result) {
			throw new BizException("任务名为" + jobName + "的任务已存在!");
		}

		SysJob bean = new SysJob();
		bean.setJobName(jobName);
		bean.setJobClassPath(jobClassPath);
		bean.setJobGroup(jobGroup);
		bean.setJobCron(jobCron);
		bean.setJobDescribe(jobDescribe);
		bean.setJobDataMap(jobDataMap);
		bean.setJobStatus(Constant.JOB_STATE.YES);
		try {
			num = sysJobService.insertSelective(bean);
		} catch (Exception e) {
			throw new BizException("新增定时任务失败");
		}
		
		SchedulerUtil.addJob(jobClassPath,jobName, jobGroup, jobCron,jobDataMap);
		return num;
	}
	
	/**
	 * 变更定时任务执行状态
	 * @return
	 * @throws Exception
	 */
	@GetMapping(value = "/changeState")
	@ResponseBody
	public int changeState(@RequestParam(value = "id") String idStr)throws Exception{
        logger.info("变更定时任务状态开始... ...");
		if (StringUtils.isBlank(idStr)) {
			throw new BizException("任务ID不能为空");
		}
		int id = Integer.parseInt(idStr);

		// 校验
		SysJob queryBean = new SysJob();
		queryBean.setId(id);
		SysJob result = sysJobService.selectByBean(queryBean);
		if (null == result) {
			throw new BizException("任务ID为" + id + "的任务不存在!");
		}
		
		SysJob updateBean = new SysJob();
		updateBean.setId(id);
		//如果是现在是启用,则停用
		if(Constant.JOB_STATE.YES == result.getJobStatus()){
			updateBean.setJobStatus(Constant.JOB_STATE.NO);
			//SchedulerUtil.jobPause(result.getJobName(), result.getJobGroup());
		  Boolean b=SchedulerUtil.isResume(result.getJobName(), result.getJobGroup());
		  if (b) {
			  SchedulerUtil.jobdelete(result.getJobName(), result.getJobGroup());
		  }
		}
		
		//如果现在是停用,则启用
		if(Constant.JOB_STATE.NO == result.getJobStatus()){
			updateBean.setJobStatus(Constant.JOB_STATE.YES);
			//SchedulerUtil.jobresume(result.getJobName(), result.getJobGroup());
			 Boolean b=SchedulerUtil.isResume(result.getJobName(), result.getJobGroup());
			 //存在则激活,不存在则添加
			  if (b) {
				  SchedulerUtil.jobresume(result.getJobName(), result.getJobGroup());
			  }else {
				  SchedulerUtil.addJob(result.getJobClassPath(),result.getJobName(), result.getJobGroup(), result.getJobCron(),result.getJobDataMap());
			}
		}
		
		try {
			sysJobService.updateByPrimaryKeySelective(updateBean);
		} catch (Exception e) {
			throw new BizException("更新数据库的定时任务信息异常!");
		}
		// 1表示成功
		return 1;
		
	}

	/**
	 * 删除一个任务
	 *
	 * @throws Exception
	 */
	@PostMapping(value = "/deleteJob")
	@ResponseBody
	public int deletejob(@RequestParam(value = "id") String idStr) throws Exception {
        logger.info("删除定时任务状态开始... ...");
		int num =0;
		if (StringUtils.isBlank(idStr)) {
			throw new BizException("任务ID不能为空");
		}
		int id = Integer.parseInt(idStr);
		
		// 存在性校验
		SysJob queryBean = new SysJob();
		queryBean.setId(id);
		SysJob result = sysJobService.selectByBean(queryBean);
		if (null == result) {
			throw new BizException("任务ID为" + idStr + "的任务不存在!");
		}
		
		try {
			num = sysJobService.deleteByPrimaryKey(id);
		} catch (Exception e) {
			throw new BizException("从数据库删除定时任务时发生异常!");
		}
		
		SchedulerUtil.jobdelete(result.getJobName(), result.getJobGroup());
		return num;
	}

	/**
	 * 修改定时表达式
	 */
	@RequestMapping("/reSchedulejob")
	@ResponseBody
	public int updateByBean(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logger.info("修改定时任务信息开始... ...");
		int num =0;
		String jobCron = request.getParameter("jobCron");
		String jobDescribe = request.getParameter("jobDescribe");
		String idStr = request.getParameter("id");
		int id = Integer.parseInt(idStr);
		// 数据非空校验
		if (!StringUtils.isNotBlank(idStr)) {
			throw new BizException("任务ID不能为空");
		}
		SysJob result = sysJobService.selectByPrimaryKey(id);
		// 数据不存在
		if (null == result) {
			throw new BizException("根据任务ID[" + id + "]未查到相应的任务记录");
		}
		SysJob bean = new SysJob();
		bean.setId(id);
		bean.setJobCron(jobCron);
		bean.setJobDescribe(jobDescribe);
		try {
			num = sysJobService.updateByPrimaryKeySelective(bean);
		} catch (Exception e) {
			throw new BizException("变更任务表达式异常:" + e.getMessage());
		}
		//只有任务状态为启用,才开始运行
		// 如果先启动再手工插入数据,此处会报空指针异常
		if( result.getJobStatus() ==Constant.JOB_STATE.YES ){
			SchedulerUtil.jobReschedule(result.getJobName(), result.getJobGroup(),jobCron);
		}
		
		// 返回成功
		return num;
	}

	/**
	 * 展示任务调度管理页
	 * 
	 * @param request
	 * @param rep
	 * @return
	 */
	@RequestMapping(value = "/jobPage", method = RequestMethod.GET)
	public String getJobPage(HttpServletRequest request, HttpServletResponse rep) {
		return "job/job_info";
	}
}

5、Quartz工具类

/**
 * Quartz工具类
 */
public class SchedulerUtil {
	private static Logger logger = LoggerFactory.getLogger(SchedulerUtil.class);

	/**
	 * 新增定时任务
	 * @param jobClassName 类路径
	 * @param jobName 任务名称
	 * @param jobGroupName 组别
	 * @param cronExpression Cron表达式
	 * @param jobDataMap 需要传递的参数
	 * @throws Exception
	 */
	public static void addJob(String jobClassName,String jobName, String jobGroupName, String cronExpression,String jobDataMap) throws Exception {
		// 通过SchedulerFactory获取一个调度器实例
		SchedulerFactory sf = new StdSchedulerFactory();
		Scheduler scheduler = sf.getScheduler();
		// 启动调度器
		scheduler.start();
		// 构建job信息
		JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass())
				.withIdentity(jobName, jobGroupName).build();
		// JobDataMap用于传递任务运行时的参数,比如定时发送邮件,可以用json形式存储收件人等等信息
		if (StringUtils.isNotEmpty(jobDataMap)) {
			JSONObject jb = JSONObject.parseObject(jobDataMap);
			Map<String, Object> dataMap =(Map<String, Object>) jb.get("data");
			for (Map.Entry<String, Object> m:dataMap.entrySet()) {
				jobDetail.getJobDataMap().put(m.getKey(),m.getValue());
			}
		}
		// 表达式调度构建器(即任务执行的时间)
		CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
		// 按新的cronExpression表达式构建一个新的trigger
		CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
				.withSchedule(scheduleBuilder).startNow().build();
		try {
			scheduler.scheduleJob(jobDetail, trigger);
		} catch (SchedulerException e) {
			logger.info("创建定时任务失败" + e);
			throw new Exception("创建定时任务失败");
		}
	}
	
	/**
	 * 停用一个定时任务
	 * @param jobName 任务名称
	 * @param jobGroupName 组别
	 * @throws Exception
	 */
	public static void jobPause(String jobName, String jobGroupName) throws Exception {
		// 通过SchedulerFactory获取一个调度器实例
		SchedulerFactory sf = new StdSchedulerFactory();
		Scheduler scheduler = sf.getScheduler();
		scheduler.pauseJob(JobKey.jobKey(jobName, jobGroupName));
	}
	
	/**
	 * 启用一个定时任务
	 * @param jobName 任务名称
	 * @param jobGroupName 组别
	 * @throws Exception
	 */
	public static void jobresume(String jobName, String jobGroupName) throws Exception {
		// 通过SchedulerFactory获取一个调度器实例
		SchedulerFactory sf = new StdSchedulerFactory();
		Scheduler scheduler = sf.getScheduler();
		scheduler.resumeJob(JobKey.jobKey(jobName, jobGroupName));
	}
	
	/**
	 * 删除一个定时任务
	 * @param jobName 任务名称
	 * @param jobGroupName 组别
	 * @throws Exception
	 */
	public static void jobdelete(String jobName, String jobGroupName) throws Exception {
		// 通过SchedulerFactory获取一个调度器实例
		SchedulerFactory sf = new StdSchedulerFactory();
		Scheduler scheduler = sf.getScheduler();
		scheduler.pauseTrigger(TriggerKey.triggerKey(jobName, jobGroupName));
		scheduler.unscheduleJob(TriggerKey.triggerKey(jobName, jobGroupName));
		scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));
	}
	
	/**
	 * 更新定时任务表达式
	 * @param jobName 任务名称
	 * @param jobGroupName 组别
	 * @param cronExpression Cron表达式
	 * @throws Exception
	 */
	public static void jobReschedule(String jobName, String jobGroupName, String cronExpression) throws Exception {
		try {
			SchedulerFactory schedulerFactory = new StdSchedulerFactory();
			Scheduler scheduler = schedulerFactory.getScheduler();
			TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
			// 表达式调度构建器
			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
			CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
			// 按新的cronExpression表达式重新构建trigger
			trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).startNow().build();
			// 按新的trigger重新设置job执行
			scheduler.rescheduleJob(triggerKey, trigger);
		} catch (SchedulerException e) {
			System.out.println("更新定时任务失败" + e);
			throw new Exception("更新定时任务失败");
		}
	}
	
	/**
	 * 检查Job是否存在
	 * @throws Exception
	 */
	public static Boolean isResume(String jobName, String jobGroupName) throws Exception {
		SchedulerFactory sf = new StdSchedulerFactory();
		Scheduler scheduler = sf.getScheduler();
		TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
		Boolean state = scheduler.checkExists(triggerKey);
	     
		return state;
	}

	/**
	 * 暂停所有任务
	 * @throws Exception
	 */
	public static void pauseAlljob() throws Exception {
		SchedulerFactory sf = new StdSchedulerFactory();
		Scheduler scheduler = sf.getScheduler();
		scheduler.pauseAll();
	}

	/**
	 * 唤醒所有任务
	 * @throws Exception
	 */
	public static void resumeAlljob() throws Exception {
		SchedulerFactory sf = new StdSchedulerFactory();
		Scheduler sched = sf.getScheduler();
		sched.resumeAll();
	}

	/**
	 * 获取Job实例
	 * @param classname
	 * @return
	 * @throws Exception
	 */
	public static BaseJob getClass(String classname) throws Exception {
		try {
			Class<?> c = Class.forName(classname);
			return (BaseJob) c.newInstance();
		} catch (Exception e) {
			throw new Exception("类["+classname+"]不存在!");
		}
		
	}

}

6、测试任务类

@DisallowConcurrentExecution
public class TestTask1 implements BaseJob {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(Thread.currentThread().getName() + " " +sdf.format(date) + " Task1: ----hello1----");
    }
}
public class TestTask2 implements BaseJob {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(Thread.currentThread().getName() + " " +sdf.format(date) + " Task2: ----hello2----");
    }
}
public class TestTask3 implements BaseJob {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private ISysJobService sysJobService;

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        logger.info("开始执行任务3... ...");
        HashMap<String,String> map = new HashMap<String,String>();
        map.put("jobGroup", "mail");
        map.put("jobStatus", "1");
        List<SysJob> jobList= sysJobService.querySysJobList(map);

        if( null == jobList || jobList.size() ==0){
            logger.info("没有状态为可用的发送邮件任务... ...");
        }

        for (SysJob sysJob:jobList) {
                logger.info("开始发送邮件... ...");
        }
    }
}

7、springboot启动初始化定时任务

/**
 * 这个类用于启动SpringBoot时,加载作业。run方法会自动执行。
 *
 * 另外可以使用 ApplicationRunner
 *
 */
public class InitStartSchedule implements CommandLineRunner {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	private ISysJobService sysJobService;
	@Autowired
	private MyJobFactory myJobFactory;
	
	@Override
	public void run(String... args) throws Exception {
		/**
		 * 用于程序启动时加载定时任务,并执行已启动的定时任务(只会执行一次,在程序启动完执行)
		 */
		
		//查询job状态为启用的
		HashMap<String,String> map = new HashMap<String,String>();
		map.put("jobStatus", "1");
		List<SysJob> jobList= sysJobService.querySysJobList(map);
		if( null == jobList || jobList.size() ==0){
			logger.info("系统启动,没有需要执行的任务... ...");
		}
		// 通过SchedulerFactory获取一个调度器实例
		SchedulerFactory sf = new StdSchedulerFactory();
		Scheduler scheduler = sf.getScheduler();
		// 如果不设置JobFactory,Service注入到Job会报空指针
		scheduler.setJobFactory(myJobFactory);
		// 启动调度器
		scheduler.start();

		for (SysJob sysJob:jobList) {
			String jobClassName=sysJob.getJobName();
			String jobGroupName=sysJob.getJobGroup();
			//构建job信息
			JobDetail jobDetail = JobBuilder.newJob(getClass(sysJob.getJobClassPath()).getClass()).withIdentity(jobClassName, jobGroupName).build();
			if (StringUtils.isNotEmpty(sysJob.getJobDataMap())) {
				JSONObject jb = JSONObject.parseObject(sysJob.getJobDataMap());
				Map<String, Object> dataMap = (Map<String, Object>)jb.get("data");
				for (Map.Entry<String, Object> m:dataMap.entrySet()) {
					jobDetail.getJobDataMap().put(m.getKey(),m.getValue());
				}
			}
			//表达式调度构建器(即任务执行的时间)
	        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(sysJob.getJobCron());
	        //按新的cronExpression表达式构建一个新的trigger
	        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobClassName, jobGroupName)
	            .withSchedule(scheduleBuilder).startNow().build();
	        // 任务不存在的时候才添加
	        if( !scheduler.checkExists(jobDetail.getKey()) ){
		        try {
		        	scheduler.scheduleJob(jobDetail, trigger);
		        } catch (SchedulerException e) {
		        	logger.info("\n创建定时任务失败"+e);
		            throw new Exception("创建定时任务失败");
		        }
	        }
		}
	}
	
	public static BaseJob getClass(String classname) throws Exception
	{
		Class<?>  c= Class.forName(classname);
		return (BaseJob)c.newInstance();
	}
}


8、自定义JobFactory,使Task注册为Bean

/**
 *
 * 将Spring的对象注入到Quartz Job 1
 */
public class AdaptableJobFactory implements JobFactory {
	@Override
	public Job newJob(TriggerFiredBundle bundle, Scheduler arg1) throws SchedulerException {
		 return newJob(bundle);
	}

	 public Job newJob(TriggerFiredBundle bundle) throws SchedulerException {
	        try {
	        	// 返回Job实例
	            Object jobObject = createJobInstance(bundle);
	            return adaptJob(jobObject);
	        }
	        catch (Exception ex) {
	            throw new SchedulerException("Job instantiation failed", ex);
	        }
	    }

	    // 通过反射的方式创建实例
	    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
	        Method getJobDetail = bundle.getClass().getMethod("getJobDetail");
	        Object jobDetail = ReflectionUtils.invokeMethod(getJobDetail, bundle);
	        Method getJobClass = jobDetail.getClass().getMethod("getJobClass");
	        Class jobClass = (Class) ReflectionUtils.invokeMethod(getJobClass, jobDetail);
	        return jobClass.newInstance();
	    }

	    protected Job adaptJob(Object jobObject) throws Exception {
	        if (jobObject instanceof Job) {
	            return (Job) jobObject;
	        }
	        else if (jobObject instanceof Runnable) {
	            return new DelegatingJob((Runnable) jobObject);
	        }
	        else {
	            throw new IllegalArgumentException("Unable to execute job class [" + jobObject.getClass().getName() +
	                    "]: only [org.quartz.Job] and [java.lang.Runnable] supported.");
	        }
	    }
}
/**
 *
 * 将Spring的对象注入到Quartz Job 2
 */
@Component
public class MyJobFactory extends AdaptableJobFactory {
    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //调用父类的方法
        Object jobInstance = super.createJobInstance(bundle);
        // 使我们的JOB注入到Spring容器
        capableBeanFactory.autowireBean(jobInstance);

        return jobInstance;
    }
}

9、省略的内容

此处省略了SysJob实体类,以及Mapper等对数据库的操作。

10、总结

本文只是大致实现一个基于Quartz实现定时任务的动态调度,具体细节还有很多未完善的。自己实现一个任务调度器的话,非常麻烦细节很多。
实现任务调度,推荐使用xxl-job分布式任务调度,或者Elastic-job。

如果需求实在是想要动态的创建任务,修改任务,那么目前为止我还没有更好的办法,只能参考该文章了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/802059.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PoseiSwap 即将开启质押,利好刺激下 POSE通证短时涨超 30%

随着Nautilus Chain主网的上线&#xff0c;预示着Web3世界迎来全新的模块化、Layer3时代&#xff0c;为Web3世界与Web2世界的深入融合构建基础。而PoseiSwap作为Nautilus Chain上的首个DEX&#xff0c;也成为了加密行业首个以模块化为基础构建的DEX。 基于Nautilus Chain&#…

免费商城搭建之java商城 开源java电子商务Spring Cloud+Spring Boot+mybatis+MQ+VR全景+b2b2c 手机商城免费搭建

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…

【lesson6】gcc和动静态库介绍

文章目录 gcc介绍gcc使用gcc -Egcc -Sgcc -cgcc *.ogcc *.c一步到位翻译 动静态库介绍动态链接和静态链接动态链接静态链接动静态链接总结 gcc介绍 gcc是一款翻译器&#xff0c;专门用来翻译C语言文本的翻译器。 程序的翻译过程&#xff1a; 是C文本---->计算机二进制可执行…

Qt5.14.2+VS2019配置MSVC2017

问题&#xff1a; The compiler " Microsoft Visual C Compiler 16 . 11 . 32106 . 194 ( amd64 x86 )( x86-windows-msvc2019-pe-32bit ) cannot produce code for the Qt version " Qt5.14.2 MSVC2017 64bit " ( x86-windows-msvc2017-pe-64bit 编译器“…

Docker安装部署ShardingProxy详细教程

&#x1f680; ShardingSphere &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&…

求整数中的最大值

才用打擂台的形式&#xff0c;先放一个数max在擂台上&#xff0c;然后每个数都上去和他比较&#xff0c;如果上去的那个数比max大&#xff0c;那么就把max替换成那个数站在擂台上&#xff0c;依次比较&#xff0c;直到所有数都比较完后&#xff0c;站在擂台上的那个max就是最大…

码元、波特率、比特率、频带利用率及数字通信系统的可靠性指标

文章目录 前言一、码元二、码元传输速率 R B R_B RB​&#xff08;传码率、波特率&#xff09;三、信息传输速率 R b R_b Rb​&#xff08;传信率&#xff0c;比特率&#xff09;四、 R B R_B RB​ 和 R b R_b Rb​的关系——H&#xff08;信源的熵&#xff09; 为纽带五、频带…

四步从菜鸟到高手,Python编程真的很简单(送书第一期:文末送书2本)

&#x1f341;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; &#x1f40b; 希望大家多多支持&#xff0c;我…

使用多个神经网络进行细菌分类(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Web课堂笔记

Web课堂笔记 文章目录 Web课堂笔记第一周html部分CSS部分php部分 第二周B/S工作原理http协议**块标记** 第三周标准盒状模型标签优先级**伪类选择器**伪元素派生选择器 第四周Flex布局多媒体查询下拉菜单作业 第五周创建一个NodeLocalStorage 和 SessionStorge 异同JQuery作业 …

小米面试题解析:深入剖析final、finally、finalize的区别

大家好&#xff0c;我是你们的小米&#xff01;今天我要和大家聊一聊在编程中常常让人迷惑的三个词&#xff1a;final、finally 和 finalize。这些词看似相似&#xff0c;但实际上在Java编程中有着截然不同的作用。如果你在面试中遇到相关问题&#xff0c;可千万别慌张&#xf…

ppt怎么压缩到10m以内?分享好用的压缩方法

PPT是一种常见的演示文稿格式&#xff0c;有时候文件过大&#xff0c;我们会遇到无法发送、上传的现象&#xff0c;这时候简单的解决方法就是压缩其大小&#xff0c;那怎么才能将PPT压缩到10M以内呢&#xff1f; PPT文件大小受到影响的主要因素就是以下几点&#xff1a; 1、图…

Mac下certificate verify failed: unable to get local issuer certificate

出现这个问题&#xff0c;可以安装证书 在finder中查找 Install Certificates.command找到后双击&#xff0c;或者使用其他终端打开 安装完即可

面试还不懂 Redis 与 MySQL 数据一致性,看这篇就够了

1. 什么是数据库与缓存一致性 数据一致性指的是&#xff1a; 缓存中存有数据&#xff0c;缓存的数据值 数据库中的值&#xff1b; 缓存中没有该数据&#xff0c;数据库中的值 最新值。 反推缓存与数据库不一致&#xff1a; 缓存的数据值 ≠ 数据库中的值&#xff1b; 缓…

【雕爷学编程】MicroPython动手做(11)——搭建掌控板IDE开发环境四种

为了能够打好基础&#xff0c;系统学习MicroPython&#xff0c;特地入手了二块掌控板 知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通…

数字人解决方案——NeRF实现实时对话数字人环境配置与源码

前言 1.这是一个能实时对话的虚拟数字人demo,使用的是NeRF&#xff08;Neural Radiance Fields&#xff09;&#xff0c;训练方式可以看看我前面的博客。 2.文本转语音是用了VITS语音合成&#xff0c;项目git:https://github.com/jaywalnut310/vits . 3.语言模型是用了新开…

Oracle 最高安全架构

​在当今世界中&#xff0c;数据库是存储敏感信息的宝贵资料库&#xff0c;攻击者总是在寻找目标。这导致网络安全威胁的增加&#xff0c;因此有必要采取适当的保护措施。Oracle Maximum Security Architecture&#xff08;MSA&#xff09;就是一种提供数据库端到端安全的解决方…

YOLOv6 论文学习

1. 解决了什么问题&#xff1f; 吸收了学术圈和工业界最新的目标检测方法&#xff0c;包括网络结构、训练策略、测试技巧、量化和优化方法。 作者有如下几点发现&#xff1a; 目前还没有人深入研究 RepVGG 重参数化对检测任务的影响。直接缩放 RepVGG 模块的效果并不好&…

苹果电脑系统优化工具:Ventura Cache Cleaner for mac

Ventura Cache Cleaner for Mac是一款专门为苹果电脑开发的系统优化工具&#xff0c;旨在帮助用户清理和优化Mac电脑&#xff0c;提高系统性能和速度。该软件由美国公司Northern Softworks开发&#xff0c;已经推出了多个版本&#xff0c;适用于不同版本的Mac操作系统。 Ventu…

pdf文件太大了不能上传怎么办?这几招值得学

PDF文件是一种常见的文档格式&#xff0c;但有时会遇到文件太大无法上传的问题&#xff0c;这时候简单的做法就是直接压缩文件的大小&#xff0c;但很多朋友还不知道怎么操作&#xff0c;下面就给大家介绍几个简单好用的&#xff0c;一起来看看吧。 工具一、嗨格式压缩大师 这…