美文网首页Java web
框架篇-定时任务(三) - Quartz

框架篇-定时任务(三) - Quartz

作者: 秃头猿猿 | 来源:发表于2020-04-28 16:14 被阅读0次

    1.定时任务 - Quartz

    1.1 前言

    我们刚刚使用Spring自带的定时任务,Spring除了自己的定时任务以外,还可以继承其他的定时任务,如: Quartz

    1.2 介绍

    image-20200428134540388.png

    Quartz核心对象如下:

    • Job 任务。表示一个工作,为要具体执行的内容。由希望由调度程序执行的组件实现的接口
    • JobDetail 任务细节。 表示一个具体可执行的调度程序,Job是这个可执行的调度程序所执行的的内容。JobDetail还包含了这个任务调度的方案和策略
    • Trigger 触发器,执行任务的规则,比如每天执行,或者每小时执行。
    • Scheduler 任务调度者。代表了一个调度容器。在一个调度容器中可以注册多个JobDetail和Trigger。当Trigger与JobDetail组合时,就可以被Scheduler容器所调度了。

    1.3 Helloword

    1.3.1 创建SpringBoot项目

    image-20200428140415152.png image-20200428151222677.png

    1.3.2 查看Quartz依赖

    • 因为Quartz启动器

      image-20200428144126550.png

    1.3.3 修改启动类

    在启动类上添加开启任务调度

    image-20200428141537527.png

    1.3.4 撰写自定义任务

    package com.task.quartz.task;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class HelloTask {
        public static void task() {
            System.out.println("=========任务1=========");
        }
    }
    
    

    1.3.5 配置Job

    通过先前我们只要,一个任务需要实现Job接口,上面的HelloTask并没有实现Job接口。也就是Quartz并不知道我们的HelloTask 是一个 Job。因此我们可以借助MethodInvokingJobDetailFactoryBean来进行配置JobDetail

    JobDetail 任务细节,里面包含了 任务,以及任务调度的方案和策略。例如是否并发执行等等。

    @Bean
        public MethodInvokingJobDetailFactoryBean jobDetail(HelloTask task) {
            MethodInvokingJobDetailFactoryBean detailFactoryBean = new MethodInvokingJobDetailFactoryBean();
            // 设置是否并发运行
            detailFactoryBean.setConcurrent(true);
            // 注册 Job
            detailFactoryBean.setTargetClass(HelloTask.class);
            // 设置要执行的方法
            detailFactoryBean.setTargetMethod("task");
            return detailFactoryBean;
    
        }
    

    1.3.6 配置触发器

    • cron触发器(CronTriggerFactoryBean)

      触发器需要与JobDetail组合

        @Bean
          public CronTriggerFactoryBean cronTrigger(MethodInvokingJobDetailFactoryBean methodInvokingJobDetailFactoryBean) {
      
              CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
              // 注册 JobDetail
              cronTriggerFactoryBean.setJobDetail(methodInvokingJobDetailFactoryBean.getObject());
              // 设置执行周期
              cronTriggerFactoryBean.setCronExpression("0/1 * * * * ?");
              return cronTriggerFactoryBean;
          }
      
    • 普通触发器(SimpleTriggerBean)

      @Bean
          public SimpleTriggerFactoryBean simpleTrigger(MethodInvokingJobDetailFactoryBean factoryBean) {
              SimpleTriggerFactoryBean simpleTriggerFactoryBean = new SimpleTriggerFactoryBean();
              simpleTriggerFactoryBean.setJobDetail(factoryBean.getObject());
              simpleTriggerFactoryBean.setStartDelay(1000);
              return  simpleTriggerFactoryBean;
          }
      

      注意:两个触发器,用一个即可。

    1.3.7 配置 Scheduler 容器

    @Bean
        public SchedulerFactoryBean scheduler(Trigger cronTrigger) {
            SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
            // 容器启动时,会更新已经存在的任务
            factoryBean.setOverwriteExistingJobs(true);
            // 设置延迟启动时间 单位 秒
            factoryBean.setStartupDelay(1);
            // 注册触发器
            factoryBean.setTriggers(cronTrigger);
            return factoryBean;
        }
    
    

    1.3.8 启动测试

    image-20200428150745359.png

    这样我们一个Helloword就写完了。但是我们发现有一些缺点:

    • 任务里面的方法必须是静态方法
    • 同时需要进行大量的配置

    因此我们有没有方式去简化呢,当然是有的的。

    1.4 简化 Hellworld

    为了更加直观测试,我们重新创建一个项目。

    1.4.1 创建项目

    image-20200428151131872.png

    1.4.2 修改启动类

    image-20200428152503025.png

    1.4.3 撰写任务类1

    package com.quartz.task;
    
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.springframework.scheduling.quartz.QuartzJobBean;
    
    
    public class HelloTask extends QuartzJobBean {
        /**
        *  在这个方法里面撰写
        */
        @Override
        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
            System.out.println("=====任务1 开始======");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("=====任务1 结束======");
        }
    }
    
    

    1.4.4 撰写任务类2

    package com.quartz.task;
    
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.springframework.scheduling.quartz.QuartzJobBean;
    
    
    public class HelloTask2 extends QuartzJobBean {
        /**
        *  在这个方法里面撰写
        */
        @Override
        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
            System.out.println("=====任务2 开始======");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("=====任务2 结束======");
        }
    }
    
    

    1.4.5 撰写配置类

    package com.quartz.task;
    
    import org.quartz.*;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class TaskConfig {
        /**
         *  创建 JobDetail 绑定任务1
         *
         */
        @Bean
        public JobDetail uploadTaskDetail() {
            return JobBuilder.newJob(HelloTask.class).withIdentity("helloTask").storeDurably().build();
        }
    
        /**
         *  创建 JobDetail 绑定任务2
         *
         */
        @Bean
        public JobDetail uploadTaskDetail2() {
            return JobBuilder.newJob(HelloTask2.class).withIdentity("helloTask2").storeDurably().build();
        }
    
        /**
         * 创建触发器 1 并且与任务1绑定
         * @return
         */
        @Bean
        public Trigger updateTaskTrigger() {
            CronScheduleBuilder builder = CronScheduleBuilder.cronSchedule("*/2 * * * * ?");
            return TriggerBuilder.newTrigger().forJob(uploadTaskDetail()).withSchedule(builder).build();
        }
    
        /**
         * 创建触发器 2 并且与任务2绑定
         * @return
         */
        @Bean
        public Trigger updateTaskTrigger2() {
            CronScheduleBuilder builder = CronScheduleBuilder.cronSchedule("*/2 * * * * ?");
            return TriggerBuilder.newTrigger().forJob(uploadTaskDetail2()).withSchedule(builder).build();
        }
    }
    
    

    1.4.6 测试

    image-20200428155113968.png

    满足我们的要求。

    1.5 线程池执行任务

    代码地址:https://github.com/smallCodeWangzh/quartz-demo-task.git

    1.5.1创建项目

    还是一样,我们创建一个新的项目,并且导入Quartz依赖。这里就不截图演示了。

    image-20200428155528515.png

    1.5.2 添加任务

    我们创建两个service。一个是GoodsService,一个是BrandService这两个Service的方法彼此执行各自查询功能

    image-20200428161935300.png
    package com.quartz.task.service;
    
    import org.springframework.stereotype.Service;
    
    @Service
    public class GoodsService {
        public void findAllGoods() {
            System.out.println("====任务1: 查询 商品 开始=====");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("=======任务1: 查询商品结束 ==========");
        }
    }
    
    

    创建 BrandService

    image-20200428162110199.png
    package com.quartz.task.service;
    
    import org.springframework.stereotype.Service;
    
    @Service
    public class BrandService {
        public void findAllBrand() {
            System.out.println("====任务2: 查询 品牌 开始=====");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("=======任务2: 查询 品牌 结束 ==========");
        }
    }
    
    

    1.5.3 编写工具类

    由于我们的Service对象由Spring容器管理,所以我们需要编写一个工具类用来获取容器里面的对象

    image-20200428162848565.png
    package com.quartz.task.util;
    
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    @Component
    public class TaskUtil implements ApplicationContextAware {
        private static ApplicationContext applicationContext;
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            TaskUtil.applicationContext = applicationContext;
        }
        public static Object getBean(String name) {
            return applicationContext.getBean(name);
        }
    
        public static <T> T getBean(String name, Class<T> requiredType) {
            return applicationContext.getBean(name, requiredType);
        }
        
        public static <T> T getBean(Class<T> requiredType) {
            return applicationContext.getBean(requiredType);
        }
    }
    
    

    1.5.4 增加JobDetail

    image-20200428162231656.png
    package com.quartz.task;
    
    import com.quartz.task.service.BrandService;
    import com.quartz.task.service.GoodsService;
    import com.quartz.task.util.TaskUtil;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.springframework.scheduling.quartz.QuartzJobBean;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ScheduleJob extends QuartzJobBean {
        // 创建线程池
        private ExecutorService executorService = Executors.newFixedThreadPool(10);
    
        @Override
        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
            GoodsService goodsService = TaskUtil.getBean(GoodsService.class);
            BrandService brandService = TaskUtil.getBean(BrandService.class);
            executorService.execute(() -> {
                goodsService.findAllGoods();
            });
            executorService.execute(() ->{
                brandService.findAllBrand();
            });
        }
    }
    
    

    1.5.5 增加配置

    package com.quartz.task.config;
    
    import com.quartz.task.ScheduleJob;
    import org.quartz.*;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class TaskConfig {
        /**
         *  创建 JobDetail 绑定任务1
         *
         */
        @Bean
        public JobDetail uploadTaskDetail() {
            return JobBuilder.newJob(ScheduleJob.class).withIdentity("helloTask").storeDurably().build();
        }
    
    
        /**
         * 创建触发器 1 并且与任务1绑定
         * @return
         */
        @Bean
        public Trigger updateTaskTrigger() {
            CronScheduleBuilder builder = CronScheduleBuilder.cronSchedule("*/10 * * * * ?");
            return TriggerBuilder.newTrigger().forJob(uploadTaskDetail()).withSchedule(builder).build();
        }
    
    }
    
    
    image-20200428163819404.png

    当然上述做法,在实际项目中不合理,有很多需要优化的地方,这只是提供一个思路。

    同时上述只是定时任务入门,如果牵涉到分布式定时任务,或者说项目需求是动态的定时任务,那么就需要视需求而定。

    相关文章

      网友评论

        本文标题:框架篇-定时任务(三) - Quartz

        本文链接:https://www.haomeiwen.com/subject/qyfbwhtx.html