quartz定时器的处理

Posted 捡黄金的少年

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了quartz定时器的处理相关的知识,希望对你有一定的参考价值。

一、quartz的简单配置

1、引入定时器的maven包

      <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.1</version>
        </dependency>

        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz-jobs</artifactId>
            <version>2.2.1</version>
        </dependency>
        <!--        >>>>SpringBoot替换为:>>>>-->
        <!--quartz依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

2、加注解简单实用

@Component
@Configurable
@EnableScheduling   //启动定时任务
public class SendMailQuartz {

    //日志对象
    private static final Logger logger = LogManager.getLogger(SendMailQuartz.class);



    //每5秒执行一次
    @Scheduled(cron = "*/5 * *  * * * ")
    public void reportCurrentByCron(){

        logger.info("定时器运行五秒一次!!!");
    }
    @Scheduled(fixedDelay = 100*30)
    public void test() {
        System.out.println("定时器第一个执行");
    }

    //每5秒执行一次
    @Scheduled(cron = "*/5 * *  * * * ")
    public void test2() {
        System.out.println("定时器第二个执行");
    }
    //每1分针执行一次
    @Scheduled(cron = "0 */1  *  * * * ")
    public void test3() {
        System.out.println("定时器第三个执行");
    }

}

二、实用xml方式引入定时器Quartz

1、上面引入了Quartz的依赖,下面的方法都实用上面的依赖,开始引入spring-mvc.xml,里面引入spring-quartz.xml

<import resource="spring-quartz.xml"/>

 完整的spring-mvc.xml如下所示

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.2.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/task
       http://www.springframework.org/schema/task/spring-task.xsd">

    <context:annotation-config/>
    <!-- 利用import引入定时器的文件 -->
    <import resource="spring-quartz.xml"/>



</beans>
2、引入spring-quartz.xml文件

(1)、这个引入所执行的类,并注入到Spring容器中, 中间的这个,如果要做动态调度执行时间,则不能缺少,<property name="scheduler" ref="scheduler"></property>

<bean id="taskJob" class="com.nswi.quartz.TestTask">
        <property name="scheduler" ref="scheduler"></property>
    </bean>

(2)、通过Id引入上面的类,并 导入需要执行的类中的方法

 <bean id="jobDetail"
          class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">

        <property name="targetObject">
            <ref bean="taskJob"/>
        </property>
<!--        执行的方法-->
        <property name="targetMethod">
            <value>run</value>
        </property>
        <!-- 上一次未执行完成的,要等待有再执行。 -->
        <property name="concurrent" value="false"></property>
    </bean>

 (3)、执行调度方法的执行时间,和执行次数都可以在这个里面配置,配置的value是毫秒,一秒等于1000毫秒,以及执行重复次数repeatCount,调用间隔时间repeatInterval

    <bean id="testTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
        <property name="jobDetail" ref="jobDetail"></property>
        <!--       启动开始时间延迟一秒-->
        <property name="startDelay" value="1000"></property>
        <!--     每次时间间隔2秒  2000 -->
     <!--        时间间隔俩分钟-->
        <property name="repeatInterval" value="20000"></property>
<!--     执行重复次数-->
<!--        <property name="repeatCount" value="0" />-->
    </bean>

(4)、调度工厂执行调度任务 

 <bean id="scheduler"
          class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="testTrigger"/>
            </list>
        </property>
    </bean>

 完整的spring-quartz.xml如下所示

<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.0.xsd">

<!--    <bean id="taskJob" class="com.nswi.quartz.TestTask"/>-->

    <bean id="taskJob" class="com.nswi.quartz.TestTask">
        <property name="scheduler" ref="scheduler"></property>
    </bean>

    <bean id="jobDetail"
          class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">

        <property name="targetObject">
            <ref bean="taskJob"/>
        </property>
<!--        执行的方法-->
        <property name="targetMethod">
            <value>run</value>
        </property>
        <!-- 上一次未执行完成的,要等待有再执行。 -->
        <property name="concurrent" value="false"></property>
    </bean>

    <!--  调度触发器 -->
<!--    <bean id="myTrigger"-->
<!--          class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">-->
<!--        <property name="jobDetail">-->
<!--            <ref bean="jobDetail" />-->
<!--        </property>-->
<!--        <property name="cronExpression">-->
<!--            <value>0/5 * * * * ?</value>-->
<!--        </property>-->
<!--    </bean>-->
    <bean id="testTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
        <property name="jobDetail" ref="jobDetail"></property>
        <!--       启动开始时间延迟一秒-->
        <property name="startDelay" value="1000"></property>
        <!--     每次时间间隔2秒  2000 -->
     <!--        时间间隔俩分钟-->
        <property name="repeatInterval" value="20000"></property>
<!--     执行重复次数-->
<!--        <property name="repeatCount" value="0" />-->
    </bean>

    <!-- 调度工厂 -->
    <bean id="scheduler"
          class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="testTrigger"/>
            </list>
        </property>
    </bean>
</beans>

3、在启动器上面引入spring-mvc.xml,间接引入spring-quartz.xml

@SpringBootApplication
@ImportResource(locations={"classpath:spring-mvc.xml"})
public class QuartzCgqApplication {
    public static void main(String[] args) {
        SpringApplication.run(QuartzCgqApplication.class, args);
    }
}

4、配置执行类,和执行方法,这样,启动后会自动执行,这个类下面标记的方法,这里设置的是run方法,

package com.nswi.quartz;

import com.nswi.entity.CgqCall;
import com.nswi.entity.CgqType;
import com.nswi.entity.CgqTypeDetails;
import com.nswi.mapper.CgqTypeMapper;
import com.nswi.mod.ModbusRtuMaster;
import com.nswi.service.ICgqCallService;
import com.nswi.service.ICgqTypeDetailsService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerKey;
import org.quartz.impl.triggers.SimpleTriggerImpl;
import org.springframework.beans.factory.annotation.Autowired;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * 描述:定时器类
 */
public class TestTask {


    

    public void run()  {

      System.out.println("执行了这个方法");


    }

    private Scheduler scheduler;

    public Scheduler getScheduler() {
        return scheduler;
    }

    public void setScheduler(Scheduler scheduler) {
        this.scheduler = scheduler;
    }

    public void restJob(long time) {

        TriggerKey triggerKey = new TriggerKey("testTrigger", Scheduler.DEFAULT_GROUP);
        SimpleTriggerImpl simpleTrigger = null;
        try {
            simpleTrigger = (SimpleTriggerImpl) scheduler.getTrigger(triggerKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }

        simpleTrigger.setRepeatInterval(time);

        try {
            scheduler.rescheduleJob(triggerKey, simpleTrigger);
        } catch (SchedulerException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


}

下面的配置,是为了动态调用方法,用来改变quartz执行的间隔时间 

   private Scheduler scheduler;

    public Scheduler getScheduler() {
        return scheduler;
    }

    public void setScheduler(Scheduler scheduler) {
        this.scheduler = scheduler;
    }

    public void restJob(long time) {

        TriggerKey triggerKey = new TriggerKey("testTrigger", Scheduler.DEFAULT_GROUP);
        SimpleTriggerImpl simpleTrigger = null;
        try {
            simpleTrigger = (SimpleTriggerImpl) scheduler.getTrigger(triggerKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }

        simpleTrigger.setRepeatInterval(time);

        try {
            scheduler.rescheduleJob(triggerKey, simpleTrigger);
        } catch (SchedulerException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

5、动态调用接口,来改变quartz的执行间隔

其中value的taskJob来自于xml中的配置


    @Autowired
    @Qualifier(value = "taskJob")
    private TestTask job;
  <bean id="taskJob" class="com.nswi.quartz.TestTask">
        <property name="scheduler" ref="scheduler"></property>
    </bean>

6、 完整的接口配置,如下所示,通过简单调用接口方式,动态调节定时器执行时间


    @Autowired
    @Qualifier(value = "taskJob")
    private TestTask job;

    //两分钟一次
    private static Long Time1 = 120000L;
    //五分钟一次
    private static Long Time2 = 300000L;
    //10分钟一次
    private static Long Time3 = 600000L;

    @GetMapping("/job/{time}/{key}")
    @ResponseBody
    public Result job(@PathVariable("time") Long time,@PathVariable("key") String key) {

        if (key.equals("wwjsj")&& time!=null) {
            if(time==2){
                System.out.println("调整为2分钟");
                job.restJob(Time1);
            }else  if(time==5){
                System.out.println("调整为5分钟");
                job.restJob(Time2);
            }else {
                System.out.println("调整为10分钟");
                job.restJob(Time3);
            }

        }

        System.out.println(time);
        return Result.ok(key);
    }

三、自定义定时器的启动,删除,暂停

工具类如下所示

 1、定时器的增加的方法

 private static SchedulerFactory schedulerFactory = new StdSchedulerFactory();

    /**
     * @param jobName          任务名
     * @param jobGroupName     任务组名
     * @param triggerName      触发器名
     * @param triggerGroupName 触发器组名
     * @param jobClass         任务
     * @param cron             时间设置,参考quartz说明文档
     * @Description: 添加一个定时任务
     */
    //SuppressWarnings  用于抑制编译器产生警告信息
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static void addJob(String jobName, String jobGroupName,
                              String triggerName, String triggerGroupName, Class jobClass, String cron) {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            // 任务名,任务组,任务执行类
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
            // 触发器
            TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
            // 触发器名,触发器组
            triggerBuilder.withIdentity(triggerName, triggerGroupName);
            triggerBuilder.startNow();
            // 触发器时间设定
            triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
            // 创建Trigger对象
            CronTrigger trigger = (CronTrigger) triggerBuilder.build();

            // 调度容器设置JobDetail和Trigger
            sched.scheduleJob(jobDetail, trigger);

            // 启动
            if (!sched.isShutdown()) {
                sched.start();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

2、定时器修改触发时间

    /**
     * @param jobName
     * @param jobGroupName
     * @param triggerName      触发器名
     * @param triggerGroupName 触发器组名
     * @param cron             时间设置,参考quartz说明文档
     * @Description: 修改一个任务的触发时间
     */
    public static void modifyJobTime(String jobName,
                                     String jobGroupName, String triggerName, String triggerGroupName, String cron) {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
            CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
            if (trigger == null) {
                return;
            }

            String oldTime = trigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(cron)) {
                /** 方式一 :调用 rescheduleJob 开始 */
                // 触发器
                TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
                // 触发器名,触发器组
                triggerBuilder.withIdentity(triggerName, triggerGroupName);
                triggerBuilder.startNow();
                // 触发器时间设定
                triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
                // 创建Trigger对象
                trigger = (CronTrigger) triggerBuilder.build();
                // 方式一 :修改一个任务的触发时间
                sched.rescheduleJob(triggerKey, trigger);
                /** 方式一 :调用 rescheduleJob 结束 */

                /** 方式二:先删除,然后在创建一个新的Job  */
                //JobDetail jobDetail = sched.getJobDetail(JobKey.jobKey(jobName, jobGroupName));
                //Class<? extends Job> jobClass = jobDetail.getJobClass();
                //removeJob(jobName, jobGroupName, triggerName, triggerGroupName);
                //addJob(jobName, jobGroupName, triggerName, triggerGroupName, jobClass, cron);
                /** 方式二 :先删除,然后在创建一个新的Job */
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

3、移除某一任务

  /**
     * @param jobName
     * @param jobGroupName
     * @param triggerName
     * @param triggerGroupName
     * @Description: 移除一个任务
     */
    public static void removeJob(String jobName, String jobGroupName,
                                 String triggerName, String triggerGroupName) {
        try {
            Scheduler sched = schedulerFactory.getScheduler();

            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);

            sched.pauseTrigger(triggerKey);// 停止触发器
            sched.unscheduleJob(triggerKey);// 移除触发器
            sched.deleteJob(JobKey.jobKey(jobName, jobGroupName));// 删除任务
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

4、启动所有定时任务

   /**
     * @Description:启动所有定时任务
     */
    public static void startJobs() {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            sched.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

5、关闭所有定时器

 /**
     * @Description:关闭所有定时任务
     */
    public static void shutdownJobs() {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            if (!sched.isShutdown()) {
                sched.shutdown();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

这个工具类完整的如下

package com.nswi.quartz;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class QuartzManager {

    private static SchedulerFactory schedulerFactory = new StdSchedulerFactory();

    /**
     * @param jobName          任务名
     * @param jobGroupName     任务组名
     * @param triggerName      触发器名
     * @param triggerGroupName 触发器组名
     * @param jobClass         任务
     * @param cron             时间设置,参考quartz说明文档
     * @Description: 添加一个定时任务
     */
    //SuppressWarnings  用于抑制编译器产生警告信息
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static void addJob(String jobName, String jobGroupName,
                              String triggerName, String triggerGroupName, Class jobClass, String cron) {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            // 任务名,任务组,任务执行类
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
            // 触发器
            TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
            // 触发器名,触发器组
            triggerBuilder.withIdentity(triggerName, triggerGroupName);
            triggerBuilder.startNow();
            // 触发器时间设定
            triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
            // 创建Trigger对象
            CronTrigger trigger = (CronTrigger) triggerBuilder.build();

            // 调度容器设置JobDetail和Trigger
            sched.scheduleJob(jobDetail, trigger);

            // 启动
            if (!sched.isShutdown()) {
                sched.start();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param jobName
     * @param jobGroupName
     * @param triggerName      触发器名
     * @param triggerGroupName 触发器组名
     * @param cron             时间设置,参考quartz说明文档
     * @Description: 修改一个任务的触发时间
     */
    public static void modifyJobTime(String jobName,
                                     String jobGroupName, String triggerName, String triggerGroupName, String cron) {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
            CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
            if (trigger == null) {
                return;
            }

            String oldTime = trigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(cron)) {
                /** 方式一 :调用 rescheduleJob 开始 */
                // 触发器
                TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
                // 触发器名,触发器组
                triggerBuilder.withIdentity(triggerName, triggerGroupName);
                triggerBuilder.startNow();
                // 触发器时间设定
                triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
                // 创建Trigger对象
                trigger = (CronTrigger) triggerBuilder.build();
                // 方式一 :修改一个任务的触发时间
                sched.rescheduleJob(triggerKey, trigger);
                /** 方式一 :调用 rescheduleJob 结束 */

                /** 方式二:先删除,然后在创建一个新的Job  */
                //JobDetail jobDetail = sched.getJobDetail(JobKey.jobKey(jobName, jobGroupName));
                //Class<? extends Job> jobClass = jobDetail.getJobClass();
                //removeJob(jobName, jobGroupName, triggerName, triggerGroupName);
                //addJob(jobName, jobGroupName, triggerName, triggerGroupName, jobClass, cron);
                /** 方式二 :先删除,然后在创建一个新的Job */
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param jobName
     * @param jobGroupName
     * @param triggerName
     * @param triggerGroupName
     * @Description: 移除一个任务
     */
    public static void removeJob(String jobName, String jobGroupName,
                                 String triggerName, String triggerGroupName) {
        try {
            Scheduler sched = schedulerFactory.getScheduler();

            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);

            sched.pauseTrigger(triggerKey);// 停止触发器
            sched.unscheduleJob(triggerKey);// 移除触发器
            sched.deleteJob(JobKey.jobKey(jobName, jobGroupName));// 删除任务
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @Description:启动所有定时任务
     */
    public static void startJobs() {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            sched.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @Description:关闭所有定时任务
     */
    public static void shutdownJobs() {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            if (!sched.isShutdown()) {
                sched.shutdown();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

执行的方法如下所示

1、执行的类继承Job类,然后实现它的excute方法

MyJob implements Job
public class MyJob implements Job {



    @Autowired
    CgqTypeMapper cgqTypeMapper;


    int i = 1;

    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        Date date = new Date();
        System.out.println(date);
        i++;
        System.out.println(i);


        cgqTypeMapper = (CgqTypeMapper) SpringContextJobUtil.getBean("cgqTypeMapper");

        List<CgqType> cgqTypes = cgqTypeMapper.selectAll();
        try {

            cgqTypes.forEach(System.out::println);
            System.out.println(cgqTypes.size() + "个");



        } catch (Exception e) {

            e.printStackTrace();
        }


    }
}

2、调用方法如下

public class Test {

    public static String JOB_NAME = "动态任务调度";
    public static String TRIGGER_NAME = "动态任务触发器";
    public static String JOB_GROUP_NAME = "XLXXCC_JOB_GROUP";
    public static String TRIGGER_GROUP_NAME = "XLXXCC_JOB_GROUP";

    public static void main(String[] args) {
        try {
            System.out.println("【系统启动】开始(每5秒输出一次)...");
            QuartzManager.addJob(JOB_NAME, JOB_GROUP_NAME, TRIGGER_NAME, TRIGGER_GROUP_NAME, MyJob.class, "0/5 * * * * ?");

//            Thread.sleep(5000);
//            System.out.println("【修改时间】开始(每5秒输出一次)...");
//            QuartzManager.modifyJobTime(JOB_NAME, JOB_GROUP_NAME, TRIGGER_NAME, TRIGGER_GROUP_NAME, "0/5 * * * * ?");

//            Thread.sleep(6000);
//            System.out.println("【移除定时】开始...");
//            QuartzManager.removeJob(JOB_NAME, JOB_GROUP_NAME, TRIGGER_NAME, TRIGGER_GROUP_NAME);
//            System.out.println("【移除定时】成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

四、对比两种动态调用定时器的不同

1、xml 方式比较灵活简单,可以定时执行某个类下面具体的方法

2、采用继承job,只能执行excute的方法,因为输入的是 MyJob.class,这样的class文件,所以如果使用Spring容器中的某个bean,会出现找不到bean的问题。所以我建立了一个方法类如下,上面的excute方法中有使用到这个方法类

@Component
public class SpringContextJobUtil implements ApplicationContextAware {

    private static ApplicationContext context;

    @Override
    @SuppressWarnings("static-access" )
    public void setApplicationContext(ApplicationContext contex)
            throws BeansException {
        // TODO Auto-generated method stub
        this.context = contex;
    }
    public static Object getBean(String beanName){
        return context.getBean(beanName);
    }

    public static String getMessage(String key){
        return context.getMessage(key, null, Locale.getDefault());
    }
}

3、引用方式如下(我这里使用MybatisPlus的Mapper层的SelectAll)

  @Autowired
 CgqTypeMapper cgqTypeMapper;



cgqTypeMapper = (CgqTypeMapper) SpringContextJobUtil.getBean("cgqTypeMapper");

List<CgqType> cgqTypes = cgqTypeMapper.selectAll();

以上是关于quartz定时器的处理的主要内容,如果未能解决你的问题,请参考以下文章

以代码的方式管理quartz定时任务的暂停重启删除添加等

定时器(quartz 一)

基于zookeeper和quartz实现分布式定时调度

javaweb,监听器和定时器(Quartz)互相调控的问题

关于quartz的同步处理问题

Spring Boot集成quartz实现定时任务并支持切换任务数据源