动态管理定时任务

要解决的问题;

之前一直是以配置参数的方式来管理定时任务,在项目加载的时候,定时任务的所有配置已加载完成,当需要改变定时任务执行时间时需要重启系统,所以我们需要一个能在线的对定时任务的增,删,改,查管理。

话不多说,直接上代码:

  1. 首先,引入相关jar包

    pom.xml 

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

     

  2. 在applicationContext.xml 中加上xml的引用

    applicationContent.xml 

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd">
     
        <import resource="quartz.xml" />
     
    </beans>

     

  3. 以下是quartz.xml中的具体内容

    quartz 

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
     
        <!-- 这个类用来做需要完成的业务-->
        <bean id="myJob" class="com.star.ott.multiplexer.tasks.BlankJob"></bean>
     
        <!-- 定时任务 -->
        <!-- 定义调用对象和调用对象的方法,这个配置和普通的一样的,id是JobDetail的名字  -->
        <bean id="jobtask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
            <!-- 调用的类  -->
            <property name="targetObject" ref="myJob" />
            <!-- 调用类中的方法  -->
            <property name="targetMethod" value="doNothing" />
            <!-- 是否并发  -->
            <property name ="concurrent" value ="false"  />
        </bean>
     
        <!-- 定义触发时间 ,这边就不同了,这里必须将时间设置成无限长,因为我们要去读取数据库的时间来做为定时器的触发时间-->
        <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean ">
            <property name="jobDetail" ref="jobtask" />
            <!-- cron表达式  -->
            <property name="cronExpression" value="0 0 0 * * ?" />
        </bean>
     
        <!-- 总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序  -->
        <bean id="startQuertz" lazy-init="true" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
            <property name="triggers">
                <list>
                    <ref bean="cronTrigger" />
                </list>
            </property>
        </bean>
     
        <!--这个类是用来设置触发时间的, startJobs方法启动调度容器,然后按照上面触发器每隔1s执行所配置的myJob2.doSomething()方法 -->
        <bean id="quartzManager" class="com.star.ott.multiplexer.tasks.QuartzManager" lazy-init="false" init-method="startJobs" >
            <!--这个对象一定要注入,这样类才能进行管理,还有在类型要用get set方法,不然会报错。-->
            <property name="scheduler" ref="startQuertz" />
        </bean>
    </beans>

     

  4. 给出类如下

    BlankJob 

    package com.star.ott.multiplexer.tasks;
     
    import org.quartz.Job;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import java.util.Date;
     
    /**
     * Created by 10000474 on 2019/11/30 0030.
     */
    public class BlankJob implements Job {
     
        @Override
        public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            //System.out.println(new Date() + ": blank job doing something...");
        }
     
        public void doNothing(){
            System.out.println(new Date() + ": blank job doing nothing...");
        }
     
     
    }

     

  5. 定时任务真正的管理类

    QuartzManager 

    package com.star.ott.multiplexer.tasks;
     
    import org.quartz.CronScheduleBuilder;
    import org.quartz.CronTrigger;
    import org.quartz.JobBuilder;
    import org.quartz.JobDetail;
    import org.quartz.JobKey;
    import org.quartz.Scheduler;
    import org.quartz.Trigger;
    import org.quartz.TriggerBuilder;
    import org.quartz.TriggerKey;
     
    public class QuartzManager {
     
        private Scheduler scheduler;
     
        public Scheduler getScheduler() {
            return scheduler;
        }
     
        public void setScheduler(Scheduler scheduler) {
            this.scheduler = scheduler;
        }
     
        /**
         * 功能: 添加一个定时任务
         * @param jobName 任务名
         * @param jobGroupName  任务组名
         * @param triggerName 触发器名
         * @param triggerGroupName 触发器组名
         * @param jobClass  任务的类类型  eg:TimedMassJob.class
         * @param cron   时间设置 表达式,参考quartz说明文档
         */
        public void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron,Object...objects) {
            try {
                // 任务名,任务组,任务执行类
                JobDetail jobDetail= JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
                // 触发器
                if(objects!=null){
                    for (int i = 0; i < objects.length; i++) {
                        //该数据可以通过Job中的JobDataMap dataMap = context.getJobDetail().getJobDataMap();来进行参数传递值
                        jobDetail.getJobDataMap().put("data"+(i+1), objects[i]);
                    }
                }
                TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
                // 触发器名,触发器组
                triggerBuilder.withIdentity(triggerName,triggerGroupName);
                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 (Exception e) {
                throw new RuntimeException(e);
            }
        }
     
        /**
         * 功能:修改一个任务的触发时间
         * @param jobName
         * @param jobGroupName
         * @param triggerName 触发器名
         * @param triggerGroupName 触发器组名
         * @param cron   时间设置,参考quartz说明文档
         */
        public void modifyJobTime(String jobName, String jobGroupName, String triggerName, String triggerGroupName, String cron) {
            try {
                TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
                CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
                if (trigger == null) {
                    return;
                }
                String oldTime = trigger.getCronExpression();
                if (!oldTime.equalsIgnoreCase(cron)) {
                    // 触发器
                    TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
                    // 触发器名,触发器组
                    triggerBuilder.withIdentity(triggerName, triggerGroupName);
                    triggerBuilder.startNow();
                    // 触发器时间设定
                    triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
                    // 创建Trigger对象
                    trigger = (CronTrigger) triggerBuilder.build();
                    // 修改一个任务的触发时间
                    scheduler.rescheduleJob(triggerKey, trigger);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
     
        /**
         * 功能: 移除一个任务
         * @param jobName
         * @param jobGroupName
         * @param triggerName
         * @param triggerGroupName
         */
        public void removeJob(String jobName, String jobGroupName,String triggerName, String triggerGroupName) {
            try {
                TriggerKey triggerKey = TriggerKey.triggerKey(triggerName,triggerGroupName);
                // 停止触发器
                scheduler.pauseTrigger(triggerKey);
                // 移除触发器
                scheduler.unscheduleJob(triggerKey);
                // 删除任务
                scheduler.deleteJob(JobKey.jobKey(jobName,jobGroupName));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
     
        /**
         *
         * 功能:启动所有定时任务
         */
        public void startJobs() {
            try {
                scheduler.start();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        /**
         * 功能:关闭所有定时任务
         */
        public void shutdownJobs() {
            try {
                if (!scheduler.isShutdown()) {
                    scheduler.shutdown();
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

     

  6. 以下类是我项目的具体业务上又封了一层,大家可以实现自己的,我的项目是记录在了文件中,大家有数据库的可以从数据库中记录当前的任务及状态

    ModJobManager 

    package com.star.ott.multiplexer.tasks;
     
    import com.alibaba.fastjson.JSONArray;
    import com.star.ott.multiplexer.domain.TimingConstant;
    import com.star.ott.multiplexer.domain.TimingDto;
    import com.star.ott.multiplexer.util.DateUtil;
    import com.star.ott.multiplexer.util.FileUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextRefreshedEvent;
    import org.springframework.stereotype.Component;
     
    import java.util.Date;
    import java.util.List;
     
    @Component
    public class ModJobManager implements ApplicationListener<ContextRefreshedEvent> {
     
        @Autowired
        private ModQuartzService modQuartzService;
     
        @Value("${timing.file.path}")
        private String TIMING_FILE_PATH;
     
     
        public void init() {
            //System.out.println("读取所有设置!");
            List<ModQuartz> jobs = modQuartzService.listAll();
            QuartzManager quartzManager = (QuartzManager) SpringContextUtil.getBean("quartzManager");
     
            //读取cron的执行时间,如果文件中没有,则取默认值;
            String cron = TimingConstant.default_cron;
            String jsonString = FileUtil.readFile(TIMING_FILE_PATH);
            if(jsonString!=null && (!"".equals(jsonString.trim()))){
                TimingDto timingDto = JSONArray.parseObject(jsonString, TimingDto.class);
                System.out.println(timingDto.getExecTime());
                Date date = DateUtil.changeTimeStrToDate(timingDto.getExecTime());
                System.out.println(date);
                cron = QuartzCronDateUtils.getCron(date);
            }
     
            System.out.println("the create job`cron is "+cron);
            quartzManager.addJob(TimingConstant.job_name,TimingConstant.job_group_name,TimingConstant.trigger_name,
                    TimingConstant.trigger_group_name,UpdateACJob.class,cron,null);
        }
     
        @Override
        public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
            init();
        }
    }

     

  7. 再赋上工具类,在界面直接编辑时间,时间可以再转成cron

    QuartzCronDateUtils 

    package com.star.ott.multiplexer.tasks;
     
    import java.text.SimpleDateFormat;
    import java.util.Date;
     
    public class QuartzCronDateUtils {
        /***
         *  功能描述:日期转换cron表达式时间格式
         * @param date
         * @param dateFormat : e.g:yyyy-MM-dd HH:mm:ss
         * @return
         */
        public static String formatDateByPattern(Date date,String dateFormat){
            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
            String formatTimeStr = null;
            if (date != null) {
                formatTimeStr = sdf.format(date);
            }
            return formatTimeStr;
        }
        /***
         * convert Date to cron ,eg.  "14 01 17 22 07 ? 2017"
         * @param date:时间点
         * @return
         */
        public static String getCron(java.util.Date  date){
            String dateFormat="ss mm HH dd * ?";
            return formatDateByPattern(date,dateFormat);
        }
    }

     

  8. 其中Quartz中有个比较坑的操作,不能直接取到springboot已注入了中的bean的实例(官网明确说了,没办法),所以我们需要自己取一下bean,这里再提供个spring上下文的工具类

    SpringContextUtil 

    package com.star.ott.multiplexer.tasks;
     
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
     
    @Component
    public class SpringContextUtil implements ApplicationContextAware {
     
        // Spring应用上下文环境
        public static ApplicationContext applicationContext;
     
        /**
         * 实现ApplicationContextAware接口的回调方法。设置上下文环境
         *
         * @param applicationContext
         */
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) {
            SpringContextUtil.applicationContext = applicationContext;
        }
     
        /**
         * @return ApplicationContext
         */
        public static ApplicationContext getApplicationContext() {
            return applicationContext;
        }
     
        /**
         * 获取对象
         *
         * @param name
         * @return Object
         * @throws BeansException
         */
        public static Object getBean(String name) throws BeansException {
            return applicationContext.getBean(name);
        }
     
    }

    取的时候用以下代码:(在ModJobManager.java 中已有示例)

    QuartzManager quartzManager = (QuartzManager) SpringContextUtil.getBean("quartzManager");

  9. 上我们真正的Job类

    UpdateACJob 

    package com.star.ott.multiplexer.tasks;
     
    import com.star.ott.multiplexer.buiness.NmxService;
    import org.quartz.Job;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
     
    public class UpdateACJob implements Job {
     
     
        @Override
        public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            System.out.println("UpdateACJob ........start");
            NmxService nmxService = (NmxService) SpringContextUtil.getBean("nmxService");
            nmxService.updateAllACValue();
            System.out.println("UpdateACJob ........end");
        }
     
        public void doNothing(){
            System.out.println("do nothing");
        }
    }

    还有个常量类

    TimingConstant 

    package com.star.ott.multiplexer.domain;
     
    /**
     * Created by 10000474 on 2019/11/30 0030.
     */
    public class TimingConstant {
     
        public static final String job_name = "updateAC";
     
        public static final String job_group_name = "updateAC_group";
     
        public static final String trigger_name = "updateAC_t";
     
        public static final String trigger_group_name = "updateAC_t_group";
     
        public static final String default_cron = "0 0 1 27 * ?";
          
    }

     

  10. 最后,再来一小段,这个是我在service层对修改定时任务时间的封装,至于,增加、删除、暂停、继续定时任务,类似这样封装一下就可以了

    business 

    /**
     * Created by 10000474 on 2019/11/22 0022.
     */
    @Service("timingInfoService")
    public class TimingInfoService {
       
        @Autowired
        QuartzManager quartzManager;
     
        public void updateJob(String execTime){
            Date date = DateUtil.changeTimeStrToDate(execTime);
            String cron = QuartzCronDateUtils.getCron(date);
     
            System.out.println("the modify job`cron is "+cron);
            quartzManager.modifyJobTime(TimingConstant.job_name,TimingConstant.job_group_name,TimingConstant.trigger_name,
                    TimingConstant.trigger_group_name,cron);
        }
    }


    到此结束,以后编辑定时任务,再也不需要重启应用了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值