Spring MVC、Spring Boot 分别集成 Quartz 动态定时任务

为什么要用Quartz

我们都知道Spring Boot自带定时器:@Scheduled(cron="0/1 * * * * ?")(记得在启动类加上注解@EnableScheduling),这样就已经实现了定时器的功能。
那么为什么还要用Quartz呢? Quartz更容易管理,在多任务时,更方便的去动态配置,能实现动态关闭开启效果。

Quartz表达式(Cron)

cron="0/1 * * * * ?"

名称是否必须允许值特殊字符
0-59- * /
0-59- * /
0-23- * /
1-31- * ? / L W C
1-12 或 JAN-DEC- * /
1-7 或 SUN-SAT- * ? / L C #
空 或 1970-2099- * /

特殊字符

特殊字符意义
*表示所有值
?表示未说明的值,即不关心它为何值
-表示一个指定的范围
,表示附加一个可能值
/符号前表示开始时间,符号后表示每次递增的值

实例

表达式意义
“0 0 12 * * ?”每天中午12点触发
“0 15 10 ? * *”每天上午10:15触发
“0 15 10 * * ?”每天上午10:15触发
“0 15 10 * * ? *”每天上午10:15触发
“0 15 10 * * ? 2005”2005年的每天上午10:15触发
“0 * 14 * * ?”在每天下午2点到下午2:59期间的每1分钟触发
“0 0/5 14 * * ?”在每天下午2点到下午2:55期间的每5分钟触发
“0 0/5 14,18 * * ?”在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
“0 0-5 14 * * ?”在每天下午2点到下午2:05期间的每1分钟触发
“0 10,44 14 ? 3 WED”每年三月的星期三的下午2:10和2:44触发
“0 15 10 ? * MON-FRI”周一至周五的上午10:15触发
“0 15 10 15 * ?”每月15日上午10:15触发
“0 15 10 L * ?”每月最后一日的上午10:15触发
“0 15 10 ? * 6L”每月的最后一个星期五上午10:15触发
“0 15 10 ? * 6L 2002-2005”2002年至2005年的每月的最后一个星期五上午10:15触发
“0 15 10 ? * 6#3”每月的第三个星期五上午10:15触发

代码走起

pom文件添加依赖包

		<!-- quartz 定时器 -->
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.3.0</version>
		</dependency>

初始注入scheduler

Spring Boot是在启动类加上@Bean的注入

	/**
	 * 初始注入scheduler
	 * @return
	 * @throws SchedulerException
	 */
	@Bean
	public Scheduler scheduler() throws SchedulerException {
		SchedulerFactory schedulerFactoryBean = new StdSchedulerFactory();
		return schedulerFactoryBean.getScheduler();
	}

Spring MVC是在applicationContext.xml加上下面代码

    <!-- ============================================定时任务========================= -->
    <!-- -------------这段是为了项目启动自动执行定时任务-----开始----------- -->
    <!-- 这个类用来做需要完成的业务-->
     <bean id="myJob2" class="com.xxx.xxxx.xxxxx.myJob2"></bean>
     <!--定义调用对象和调用对象的方法,这个配置和普通的一样的,id是JobDetail的名字-->
    <bean id="jobtask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <!-- 调用的类  -->
        <property name="targetObject" ref="myJob2" />
        <!-- 调用类中的方法  -->
        <property name="targetMethod" value="doSomething" />
        <!-- 是否并发  -->
        <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/1 * * * * ?" />
    </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.xxx.xxxxx.xxxx.QuartzScheduler" lazy-init="false" init-method="startJobs" >
        <!--这个对象一定要注入,这样类才能进行管理,还有在类型要用get set方法,不然会报错。-->
        <property name="scheduler" ref="startQuertz" />
    </bean>
    <!-- =================================定时任务================================== -->

注意:其实不管是Spring Boot 还是Spring MVC 哪种方式,都是为了交给Spring这个容器进行管理

新建一个任务调度处理类


import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

/**
 * 任务调度处理
 * Created by LMD on 2019/3/28.
 */

@Configuration
public class QuartzScheduler {

    // 任务调度
    @Autowired
    private Scheduler scheduler;

    /**
     * @Description: 添加一个定时任务
     *
     * @param jobName 任务名
     * @param jobGroupName  任务组名
     * @param triggerName 触发器名
     * @param triggerGroupName 触发器组名
     * @param jobClass  任务
     * @param cron   时间设置,参考quartz说明文档
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void addJob(String jobName, String jobGroupName,
                       String triggerName, String triggerGroupName, Class jobClass, String cron) {
        try {
            // 任务名,任务组,任务执行类
            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
            scheduler.scheduleJob(jobDetail, trigger);

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

    /**
     * @Description: 修改一个任务的触发时间
     *
     * @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)) {
                /** 方式一 :调用 rescheduleJob 开始 */
                // 触发器
                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);
                /** 方式一 :调用 rescheduleJob 结束 */

                /** 方式二:先删除,然后在创建一个新的Job  */
                //JobDetail jobDetail = scheduler.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);
        }
    }

    /**
     * @Description: 移除一个任务
     *

     * @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);
        }
    }

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

    /**
     * @Description:关闭所有定时任务
     */
    public void shutdownJobs() {
        try {
            if (!scheduler.isShutdown()) {
                scheduler.shutdown();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
测试
 @Autowired
    private QuartzScheduler quartzScheduler;
    /**
     * 开启 Quartz
     * @return
     * @throws MyException
     */
    @RequestMapping(value = "/system/quartzAll")
    public void quartzAll() throws MyException {

        try {
            System.out.println("【系统启动】开始......");

            System.out.println("【增加job1启动】开始(每1秒输出一次)...");//0/10 * * * * ?
            quartzScheduler.addJob("job1", "job1", "job1", "job1", MyJobAppoint.class, "0/1 * * * * ? *");

            Thread.sleep(5000);
            System.out.println("【修改job1时间】开始(每2秒输出一次)...");
            quartzScheduler.modifyJobTime("job1", "job1", "job1", "job1", "0/2 * * * * ?");

            Thread.sleep(10000);
            System.out.println("【移除job1定时】开始...");
            quartzScheduler.removeJob("job1", "job1", "job1", "job1");

            System.out.println("【增加job2启动】开始(每1秒输出一次)...");//0/10 * * * * ?
            quartzScheduler.addJob("job2", "job2", "job2", "job2", MyJobAppoint.class, "0/1 * * * * ? *");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    /**
     * 关闭 Quartz
     * @return
     * @throws MyException
     */
    @RequestMapping(value = "/system/closeQuartz")
    @ResponseBody
    public void closeQuartz() throws MyException {

        try {
            System.out.println("【移除全部定时】");//0/10 * * * * ?
            // 关掉任务调度容器
            quartzScheduler.shutdownJobs();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

运行项目,调用对应的接口,控制台就打印如下日志:

2019-03-28 10:56:02.448  INFO 22948 --- [-nio-80-exec-10] c.s.companycms.config.LoginInterceptor   : request: 请求地址 path [/api/system/quartzAll] uri [/api/system/quartzAll]
【系统启动】开始......
【增加job1启动】开始(1秒输出一次)...
2019-03-28 10:56:02.470  INFO 22948 --- [-nio-80-exec-10] org.quartz.core.QuartzScheduler          : Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
2019-03-28 10:56:02: MyJobAppoint 1 doing......
2019-03-28 10:56:03: MyJobAppoint 1 doing......
2019-03-28 10:56:04: MyJobAppoint 1 doing......
2019-03-28 10:56:05: MyJobAppoint 1 doing......
2019-03-28 10:56:06: MyJobAppoint 1 doing......
2019-03-28 10:56:07: MyJobAppoint 1 doing......
【修改job1时间】开始(2秒输出一次)...
2019-03-28 10:56:08: MyJobAppoint 1 doing......
2019-03-28 10:56:10: MyJobAppoint 1 doing......
2019-03-28 10:56:12: MyJobAppoint 1 doing......
2019-03-28 10:56:14: MyJobAppoint 1 doing......
2019-03-28 10:56:16: MyJobAppoint 1 doing......
【移除job1定时】开始...
【增加job2启动】开始(1秒输出一次)...
2019-03-28 10:56:17.474  INFO 22948 --- [-nio-80-exec-10] org.quartz.core.QuartzScheduler          : Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
2019-03-28 10:56:17: MyJobAppoint 1 doing......
2019-03-28 10:56:18: MyJobAppoint 1 doing......
2019-03-28 10:56:19: MyJobAppoint 1 doing......
2019-03-28 10:56:20: MyJobAppoint 1 doing......
2019-03-28 10:56:21: MyJobAppoint 1 doing......
2019-03-28 10:56:22: MyJobAppoint 1 doing......
2019-03-28 10:56:23: MyJobAppoint 1 doing......
2019-03-28 10:56:24: MyJobAppoint 1 doing......
2019-03-28 10:56:25: MyJobAppoint 1 doing......
2019-03-28 10:56:26: MyJobAppoint 1 doing......
2019-03-28 10:56:27: MyJobAppoint 1 doing......
2019-03-28 10:56:28: MyJobAppoint 1 doing......
2019-03-28 10:56:29: MyJobAppoint 1 doing......
2019-03-28 10:56:30: MyJobAppoint 1 doing......
2019-03-28 10:56:31: MyJobAppoint 1 doing......
2019-03-28 10:56:32: MyJobAppoint 1 doing......
2019-03-28 10:56:32.550  INFO 22948 --- [p-nio-80-exec-1] c.s.companycms.config.LoginInterceptor   : request: 请求地址 path [/api/system/closeQuartz] uri [/api/system/closeQuartz]
【移除全部定时】

最后

谢谢大家的参考、阅读;
可能大家在实际写代码的过程中有不一样的异常出错,大家可以留言一起讨论学习。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值