SpringBoot集成定时框架quartz实现增删改查功能,一文学懂quartz定时调度!

    科技2022-07-16  109

    记录自己学习的理解,以供日后查阅,欢迎指正不对之处!

    1、quartz四大角色概念

    1.1 Job

    实现Job接口,通过execute(JobExecutionContext context)方法定义定时任务具体干些什么。

    1.2 JobDetail

    为job添加相关信息,如Job 名字、描述、关联监听器、存入任务对应参数等信息,我们可以通过这些信息查找和操作定时任务,以及定时任务在执行时通过上下文获取参数信息。每个JobDetail的组和名称必须唯一。

    1.3 Trigger

    定义触发器名、触发器组、设定触发时间等信息。告诉调度器何时执行定时任务。每个Trigger的组和名称必须唯一。

    1.4 Scheduler

    调度器,相当于一个容器装载着JobDetail和Trigger。定义了JobDetail和Trigger的对应关系。我们可以通过组和名称访问容器中 Trigger 和 JobDetail。

    相互之间的关系:一个Job可以对应多个JobDetail,一个JobDetail可以对应多个Trigger,而一个Trigger只能对应一个JobDetail,一个Scheduler可以注册多个JobDetail和多个Trigger。

    2、简单定时器

    2.1 引入quartz核心依赖

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

    2.2 其他依赖(可选)

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>

    2.3 定义job

    实现QuartzJobBean自定义job,executeInternal方法定义job具体执行的细节。

    @Data public class SampleJob extends QuartzJobBean { private String name; @Override protected void executeInternal(JobExecutionContext context) { System.out.println(String.format("sampleJob name:%s !",this.name)); } }

    2.4定义JobDetail和Trigger。

    withIdentity:方法定义名称和组。只传一个参数时代表组名,名称自动生成。 usingJobData:方法表示设置job的参数。 storeDurably:表示持久化信息。 withIntervalInSeconds:方法表示设置多少秒执行。 repeatForever():方法表示一直重复执行。 withSchedule:注册的容器类型。

    @Configuration public class SampleScheduler { @Bean public JobDetail sampleJobDetail(){ return JobBuilder.newJob(SampleJob.class) .withIdentity("SampleJobDetail") .usingJobData("name", "Hello sample job!") .storeDurably() .build(); } @Bean public Trigger sampleJobTrigger(){ SimpleScheduleBuilder builder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(2) .repeatForever(); return TriggerBuilder.newTrigger() .forJob(sampleJobDetail()) .withIdentity("sampleTrigger") .withSchedule(builder) .build(); } }

    2.5 启动项目看看效果

    3、使用cron表达式实现简单定时功能

    3.1 自定义job

    public class CronJob implements Job { @Override public void execute(JobExecutionContext context) { System.out.println("Cron job is running ..."); } }

    3.2 添加启动job的方法

    @Component public class CronScheduler { @Autowired private SchedulerFactoryBean schedulerFactoryBean; /** * 向容器中添加定时任务 * @param cron * @throws SchedulerException */ public void scheduleJobs(String cron) throws SchedulerException { Scheduler scheduler = schedulerFactoryBean.getScheduler(); scheduleJob(scheduler,cron); } /** * 添加一个定时任务 * @param scheduler * @param cron * @throws SchedulerException */ private void scheduleJob(Scheduler scheduler,String cron) throws SchedulerException{ JobDetail jobDetail = JobBuilder.newJob(CronJob.class) .withIdentity("cronJob", "jobGroup") .build(); CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron); CronTrigger cronTrigger = TriggerBuilder.newTrigger() .withIdentity("myTrigger", "triggerGroup") .withSchedule(scheduleBuilder) .build(); scheduler.scheduleJob(jobDetail,cronTrigger); } }

    3.3 设置项目启动时启动定时任务

    @Component public class StartupRunner implements CommandLineRunner { @Autowired public CronScheduler scheduleJobs; @Override public void run(String... args) throws Exception { scheduleJobs.scheduleJobs("0/6 * * * * ?"); System.out.println(">>>>>>>>>>>>>>>定时任务开始执行<<<<<<<<<<<<<"); } }

    3.4 也可以结合@Scheduled指定时间点启动定时任务

    @Configuration @EnableScheduling @Component public class SchedulerListener { @Autowired public CronScheduler scheduleJobs; @Scheduled(cron = "0 30 11 25 11 ?") public void schedule() throws SchedulerException { scheduleJobs.scheduleJobs("0/6 * * * * ?"); } }

    3.5 启动项目看看效果

    4、复杂定时功能

    4.1 封装quartz工具类

    实现定时任务的实时查询、修改、新增、删除功能

    @Component public class QuartzManager { //任务组名 private static final String JOB_GROUP_NAME = "cron_group"; //触发器组名 private static final String TRIGGER_GROUP_NAME = "cron_trigger"; //调度工厂 @Autowired private SchedulerFactoryBean schedulerFactory; public Scheduler getScheduled(){ return schedulerFactory.getScheduler(); } /** * 添加定时任务 * @param jobName 任务名称 * @param jobClazz 任务job * @param cron 执行时间之cron表达式 * @param params 任务job参数 */ public void addJob(String jobName , Class jobClazz , String cron, HashMap<String,String> params){ try { //获取Scheduler Scheduler scheduler = getScheduled(); // 任务名,任务组,任务执行类 JobDetail jobDetail= JobBuilder.newJob(jobClazz).withIdentity(jobName, JOB_GROUP_NAME).build(); // 传参(可选) JobDataMap jobDataMap = jobDetail.getJobDataMap(); if(params != null ){ for (String key:params.keySet()){ jobDataMap.put(key, params.get(key)); } } // 触发器 TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger(); // 触发器名,触发器组 triggerBuilder.withIdentity(jobName, TRIGGER_GROUP_NAME); triggerBuilder.startNow(); // 触发器时间设定 triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron)); // 创建Trigger对象 CronTrigger trigger = (CronTrigger) triggerBuilder.build(); // 调度容器设置JobDetail和Trigger scheduler.scheduleJob(jobDetail, trigger); // 启动 if (!scheduler.isShutdown()) { scheduler.start(); } } catch (SchedulerException e) { e.printStackTrace(); } } /** * 删除定时任务 * @param jobName 任务名称 */ public void removeJob(String jobName) { try { Scheduler scheduler = getScheduled(); TriggerKey triggerKey = TriggerKey.triggerKey(jobName, TRIGGER_GROUP_NAME); scheduler.pauseTrigger(triggerKey);// 停止触发器 scheduler.unscheduleJob(triggerKey);// 移除触发器 scheduler.deleteJob(JobKey.jobKey(jobName, JOB_GROUP_NAME));// 删除任务 } catch (SchedulerException e) { e.printStackTrace(); } } /** * 启动任务 */ public void startJobs() { try { Scheduler scheduler = getScheduled(); scheduler.start(); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 删除任务 */ public void shutdownJobs() { try { Scheduler scheduler = getScheduled(); if (!scheduler.isShutdown()) { scheduler.shutdown(); } } catch (SchedulerException e) { e.printStackTrace(); } } /** * 获取所有任务 */ public List<String> getAllJob(){ List<String> jobs = new ArrayList<>(); try { Set<JobKey> jobKeys = getScheduled().getJobKeys(GroupMatcher.anyGroup()); for(JobKey jobKey:jobKeys){ jobs.add(jobKey.getName()); } } catch (SchedulerException e) { e.printStackTrace(); } return jobs; } }

    4.2 定义Job

    public class WebJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap dataMap = context.getMergedJobDataMap(); HashMap<String,String> map = new HashMap<>(); for(Object o:dataMap.keySet()){ map.put((String)o,dataMap.getString((String)o)); } System.out.println("my web job prams: "+map.toString()); } }

    4.3 定义web接口

    @RestController @RequestMapping("/quartz") public class QuartzController { @Autowired QuartzManager quartzManager; /** * 添加定时任务 * @param jobName * @return */ @GetMapping("/addJob") public Response addJob(String jobName){ String cron = "0/3 * * * * ?"; HashMap<String,String> map = new HashMap<>(); map.put("jobName",jobName); quartzManager.addJob(jobName, WebJob.class,cron,map); return new Response("job添加成功",""); } /** * 删除定时任务 * @param jobName * @return */ @GetMapping("/deleteJob") public Response deleteJob(String jobName){ quartzManager.removeJob(jobName); return new Response("job删除成功",""); } /** * 查询所有定时任务 * @param jobName * @return */ @GetMapping("/getAllJob") public Response getAllJob(String jobName){ return new Response("成功获取所有job",quartzManager.getAllJob()); } }

    4.4 定义响应类

    @Data @NoArgsConstructor @AllArgsConstructor public class Response<T> { Integer code; String message; T result; public Response (String message,T result){ this.code = 200; this.message = message; this.result = result; } }

    4.5 启动项目看看效果

    首先控制台没任何打印 访问查询接口获取正在运行的定时任务,返回结果为空 http://localhost:8080/quartz/getAllJob 添加一个job http://localhost:8080/quartz/addJob?jobName=JsonTom888

    再次访问查询接口获取正在运行的定时任务,返回了我们刚才添加的job名 http://localhost:8080/quartz/getAllJob 查看后台,有如下日志,说明定时任务在正常执行 我们尝试删除定时任务 http://localhost:8080/quartz/deleteJob?jobName=JsonTom888 再次访问查询接口获取正在运行的定时任务,返回结果为空 http://localhost:8080/quartz/getAllJob 查看后台日志,清空控制台日志,发现停止打印,说明我们已经成功终止定时任务 补充:实际项目中如果我们创建了大量定时任务,并希望下次重新部署服务后这些定时任务继续执行,我们可以将定时任务相关信息存入数据库,在项目启动时加载我们需要的定时任务,以达到重新部署的情况下不用重新创建定时任务。

    项目代码地址

    https://github.com/JsonTom888/scheduledAndQuartz/tree/master/quartzdemo

    Processed: 0.009, SQL: 8