Spring定时器时间配置_从此沉默的博客-程序员ITS203

技术标签: JAVA  Spring  

Quartz在Spring中动态设置cronExpression (spring设置动态定时任务)

    2007-07-25 13:52

    什么是动态定时任务:是由客户制定生成的,服务端只知道该去执行什么任务,但任务的定时是不确定的(是由客户制定)。

    这样总不能修改配置文件每定制个定时任务就增加一个trigger吧,即便允许客户修改配置文件,但总需要重新启动web服务啊,研究了下Quartz在Spring中的动态定时,发现

    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean" >

                  <property name="jobDetail" ref="schedulerJobDetail"/>
                  <property name="cronExpression">
                          <value>0/10 * * * * ?</value>
                  </property>
    </bean>
         中cronExpression是关键,如果可以动态设置cronExpression的值,也就说如果我们可以直接调用CronTriggerBean中设置cronExpression的方法,就可以顺利解决问题了。
    熟悉1的朋友可以跳过不看,下面2、3是动态定时任务的具体实现。
    1、Quartz在Spring中的简单配置

    Spring配置文件:

         <bean id="schedulerJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
                 <property name="targetObject" ref="scheduleInfoAction"/>
                 <property name="targetMethod" value="simpleJobTest"/>
                 <property name="concurrent" value="false"/>
         </bean>
         <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean" >
                  <property name="jobDetail" ref="schedulerJobDetail"/>
                  <property name="cronExpression">
                          <value>0/10 * * * * ?</value>
                  </property>
          </bean>
         <bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
                 <property name="triggers">
                         <list>
                                 <ref local="cronTrigger"/>
                         </list>
                 </property>
    </bean>
    在上面的配置中设定了
    ① targetMethod: 指定需要定时执行scheduleInfoAction中的simpleJobTest()方法
    ② concurrent:对于相同的JobDetail,当指定多个Trigger时, 很可能第一个job完成之前,第二个job就开始了。指定concurrent设为false,多个job不会并发运行,第二个job将不会在第一个job完成之前开始。
    ③ cronExpression:0/10 * * * * ?表示每10秒执行一次,具体可参考附表。
    ④ triggers:通过再添加其他的ref元素可在list中放置多个触发器。
    scheduleInfoAction中的simpleJobTest()方法,注意:此方法没有参数,如果scheduleInfoAction有两个方法simpleJobTest()和simpleJobTest(String argument),则spring只会去执行无参的simpleJobTest().
    public void simpleJobTest()
    {
                 log.warn("uh oh, Job is scheduled !'" + "' Success...");
         }

    2.Quartz在Spring中动态设置cronTrigger方法一
    Spring配置文件:
    <bean id="scheduleInfoAction" class="com.lively.happyoa.jobs.webapp.action.ScheduleInfoAction">
                 <property name="scheduler" ref="schedulerFactory"/>
                 <property name="scheduleInfoManager" ref="scheduleInfoManager"/>
         </bean>
         <bean id="schedulerJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
                 <property name="targetObject" ref="scheduleInfoAction"/>
                 <property name="targetMethod" value="reScheduleJob"/>
                 <property name="concurrent" value="false"/>
         </bean>
         <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean" >
                  <property name="jobDetail" ref="schedulerJobDetail"/>
                  <property name="cronExpression">
                          <value>0/10 * * * * ?</value>
                  </property>
          </bean>
         <bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
                 <property name="triggers">
                         <list>
                                 <ref local="cronTrigger"/>
                         </list>
        </property>
    </bean>
    scheduleInfoAction中的reScheduleJob ()方法及相关方法
    ① reScheduleJob读取数据库,获得自定义定时器调度时间():
         private void reScheduleJob() throws SchedulerException, ParseException
    {
                 // 运行时可通过动态注入的scheduler得到trigger
                 CronTriggerBean trigger = (CronTriggerBean) scheduler.getTrigger("cronTrigger", Scheduler.DEFAULT_GROUP);
                 String dbCronExpression = getCronExpressionFromDB();
                 String originConExpression = trigger.getCronExpression();
             // 判断从DB中取得的任务时间(dbCronExpression)和现在的quartz线程中的任务时间(originConExpression)是否相等
             // 如果相等,则表示用户并没有重新设定数据库中的任务时间,这种情况不需要重新rescheduleJob
                 if(!originConExpression.equalsIgnoreCase(dbCronExpression))
        {
                         trigger.setCronExpression(dbCronExpression);
                         scheduler.rescheduleJob("cronTrigger", Scheduler.DEFAULT_GROUP, trigger);
                 }
             // 下面是具体的job内容,可自行设置
             // executeJobDetail();
    }
    ② getCronExpressionFromDB():从数据库中获得dbCronExpression的具体代码,由于使用了scheduleInfoManager,所以要在定义相应的setter方法
         private String getCronExpressionFromDB()
    {
                 String sql="from ScheduleInfo scheduleInfo where 1=1 ";
                 sql=sql+" and scheduleInfo.infoId = '"+"1" + "'";
                 List scheduleList = scheduleInfoManager.queryScheduleInListBySql(sql);
                 ScheduleInfo scheduleInfo = (ScheduleInfo)scheduleList.get(0);
                 String dbCronExpression = scheduleInfo.getCronExpression();
                 return dbCronExpression;
    }
    ③ 在spring配置文件的scheduleInfoAction配置了相应的property(scheduler/ scheduleInfoManager),要为其设置setter方法
         private Scheduler scheduler;
         // 设值注入,通过setter方法传入被调用者的实例scheduler
         public void setScheduler(Scheduler scheduler)
    {
                 this.scheduler = scheduler;
        }
         private ScheduleInfoManager scheduleInfoManager;
         // 设值注入,通过setter方法传入被调用者的实例scheduleInfoManager
         public void setScheduleInfoManager(ScheduleInfoManager scheduleInfoManager)
    {
                 this.scheduleInfoManager = scheduleInfoManager;
         }

    3. Quartz在Spring中动态设置cronTrigger方法二
    在上面的2中我们可以看到,尽管已经可以动态进行rescheduleJob了,不过依然需要我们设置一个cronExpression,如果尝试一下拿掉spring配置中的
             <property name="cronExpression">
                      <value>0/10 * * * * ?</value>
              </property>
    则容器(如tomcat)启动时会报错。
    实际中我们希望tomcat启动时就可以直接去读数据库,拿到相应的dbCronExpression,然后定时执行一个job,而不希望配置初始的cronExpression ,观察下面的CronTriggerBean,考虑到cronExpression需要初始化,如果设定一个类InitializingCronTrigger继承CronTriggerBean,然后在这个类中做一些读取DB的初始化工作(设置cronExpression),问题就可以解决了。
    Spring配置文件:
    <bean id="scheduleInfoAction" class="com.lively.happyoa.jobs.webapp.action.ScheduleInfoAction">
                 <property name="scheduler" ref="schedulerFactory"/>
                 <property name="scheduleInfoManager" ref="scheduleInfoManager"/>
         </bean>
         <bean id="schedulerJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
                 <property name="targetObject" ref="scheduleInfoAction"/>
                 <property name="targetMethod" value="reScheduleJob"/>
                 <property name="concurrent" value="false"/>
         </bean>
        <bean id="cronTrigger" class="com.lively.happyoa.jobs.webapp.action.ScheduleInfoAction.InitializingCronTrigger">
                  <property name="jobDetail" ref="schedulerJobDetail"/>
                 <!--<property name="cronExpression">
                          <value>0/10 * * * * ?</value>
                  </property>-->
                  <property name="scheduleInfoManager" ref="scheduleInfoManager"/>
          </bean>
         <bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
                 <property name="triggers">
                         <list>
                                 <ref local="cronTrigger"/>
                         </list>
                 </property>
    </bean>
    InitializingCronTrigger中的相关方法
    注意:在注入scheduleInfoManager属性的时候,我们可以去读取DB任务时间(之所以放在setter方法中,是因为需要在设置scheduleInfoManager后进行getCronExpressionFromDB(),否则,也可以①②逻辑把放在类的构造函数中).
    注意InitializingCronTrigger必须extends CronTriggerBean.
    public class InitializingCronTrigger extends CronTriggerBean implements Serializable
    {
             private ScheduleInfoManager scheduleInfoManager;
             // 设值注入,通过setter方法传入被调用者的实例scheduleInfoManager
             public void setScheduleInfoManager(ScheduleInfoManager scheduleInfoManager)
        {
                     this.scheduleInfoManager = scheduleInfoManager;
                     // 因为在getCronExpressionFromDB使用到了scheduleInfoManager,所以
                     // 必须上一行代码设置scheduleInfoManager后进行getCronExpressionFromDB
                     String cronExpression = getCronExpressionFromDB ();    // ①
                     // 因为extends CronTriggerBean ,此处调用父类方法初始化cronExpression
                    setCronExpression(cronExpression);                     // ②
        }

             private String getCronExpressionFromDB()
        {
                     String sql="from ScheduleInfo scheduleInfo where 1=1 ";
                     sql=sql+" and scheduleInfo.infoId = '"+"1" + "'";
                     List scheduleList = scheduleInfoManager.queryScheduleInListBySql(sql);
                     ScheduleInfo scheduleInfo = (ScheduleInfo)scheduleList.get(0);
                     String dbCronExpression = scheduleInfo.getCronExpression();
                     return dbCronExpression;
        }
        ……
    }
各个时间可用值如下:

秒 0-59 , - * /

分 0-59 , - * /

小时 0-23 , - * /

日 1-31 , - * ? / L W C

月 1-12 or JAN-DEC , - * /

周几 1-7 or SUN-SAT , - * ? / L C #

年 (可选字段) empty, 1970-2099 , - * /

可用值详细分析如下:

“*”——字符可以用于所有字段,在“分”字段中设为"*"表示"每一分钟"的含义。

“?”——字符可以用在“日”和“周几”字段. 它用来指定 '不明确的值'. 这在你需要指定这两个字段中的某一个值而不是另外一个的时候会被用到。在后面的例子中可以看到其含义。

“-”——字符被用来指定一个值的范围,比如在“小时”字段中设为"10-12"表示"10点到12点"。

“,”——字符指定数个值。比如在“周几”字段中设为"MON,WED,FRI"表示"the days Monday, Wednesday, and Friday"。

“/”——字符用来指定一个值的的增加幅度. 比如在“秒”字段中设置为"0/15"表示"第0, 15, 30, 和 45秒"。而 "5/15"则表示"第5, 20, 35, 和 50". 在'/'前加"*"字符相当于指定从0秒开始. 每个字段都有一系列可以开始或结束的数值。对于“秒”和“分”字段来说,其数值范围为0到59,对于“小时”字段来说其为0到23, 对于“日”字段来说为0到31, 而对于“月”字段来说为1到12。"/"字段仅仅只是帮助你在允许的数值范围内从开始"第n"的值。

“L”——字符可用在“日”和“周几”这两个字段。它是"last"的缩写, 但是在这两个字段中有不同的含义。例如,“日”字段中的"L"表示"一个月中的最后一天" —— 对于一月就是31号对于二月来说就是28号(非闰年)。而在“周几”字段中, 它简单的表示"7" or "SAT",但是如果在“周几”字段中使用时跟在某个数字之后, 它表示"该月最后一个星期×" —— 比如"6L"表示"该月最后一个周五"。当使用'L'选项时,指定确定的列表或者范围非常重要,否则你会被结果搞糊涂的。

“W”——可用于“日”字段。用来指定历给定日期最近的工作日(周一到周五) 。比如你将“日”字段设为"15W",意为: "离该月15号最近的工作日"。因此如果15号为周六,触发器会在14号即周五调用。如果15号为周日, 触发器会在16号也就是周一触发。如果15号为周二,那么当天就会触发。然而如果你将“日”字段设为"1W", 而一号又是周六, 触发器会于下周一也就是当月的3号触发,因为它不会越过当月的值的范围边界。'W'字符只能用于“日”字段的值为单独的一天而不是一系列值的时候。

“L”和“W”可以组合用于“日”字段表示为'LW',意为"该月最后一个工作日"。

“#”—— 字符可用于“周几”字段。该字符表示“该月第几个周×”,比如"6#3"表示该月第三个周五( 6表示周五而"#3"该月第三个)。再比如: "2#1" = 表示该月第一个周一而 "4#5" = 该月第五个周三。注意如果你指定"#5"该月没有第五个“周×”,该月是不会触发的。

“C”—— 字符可用于“日”和“周几”字段,它是"calendar"的缩写。 它表示为基于相关的日历所计算出的值(如果有的话)。如果没有关联的日历, 那它等同于包含全部日历。“日”字段值为"5C"表示"日历中的第一天或者5号以后",“周几”字段值为"1C"则表示"日历中的第一天或者周日以后"。

对于“月份”字段和“周几”字段来说合法的字符都不是大小写敏感的。

附表:
"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触发
至于每个符号 看看例子就好了.很简单了.

作者“信仰的力量”

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u012729210/article/details/17099405

智能推荐

python类继承时构造函数的执行问题_智普python的博客-程序员ITS203

如果子类定义了自己的__init__构造方法函数,当子类的实例对象被创建时,子类只会执行自己的__init__方法函数,如果子类未定义自己的构造方法函数,会沿着搜索树找到父类的构造方法函数去执行父类里的构造方法函数。如子类定义了自己的构造方法函数,如果子类的构造方法函数内没有主动调用父类的构造方法函数,那父类的实例变量在子类不会在刚刚创建子类实例对象时出现了。class aa:

WPF 设置TextBox的输入法_笑我归无处的博客-程序员ITS203_wpf 切换输入法

使用WPF做Windows客户端开发时,如果需要控制输入框的输入语言,本文为读者介绍了两个控制输入模式的类,InputLanguageManager和InputMethod。用于控制输入法的语言,以及限制输入内容。

linux 连通性监控,利用ansible监控空远程机连通性的shell脚本,ansibleshell_weixin_39716921的博客-程序员ITS203

利用ansible监控空远程机连通性的shell脚本,ansibleshell#!/bin/bash#by fuqiang#2018-08-13cd /home/ansible/ansible vpn -m ping &gt; log/ping-vpn.lognum=`cat log/ping-vpn.log | grep -o 'SUCCESS' | wc -l`if [ $num -ne 2...

Node.js-http网络服务构建模块_蓬莱老仙的博客-程序员ITS203

Node.js 中提供了 http 模块,通过该模块可以用来构建服务器;http可以充当两个角色:可以是服务器,也可以是客户端## http 模块相关方法##### 创建服务器对象**[http.createServer([options][, requestlistener])](http://nodejs.cn/api/http.html#http_http_createserver_options_requestlistener)** 该方法用于创建一个 http 服务;返回值为一个服务器对

安装OEM遇到问题ORA-06512的处理方法_congyi3225的博客-程序员ITS203

一、emca -config dbcontrol db报错信息, 错误信息集中在创建资料库的时候,即 emca -repos create :CONFIG: ORA-20001: SYSMAN already exists...

autojs内存处理机制代码,避免经常崩溃_九黎AJ的博客-程序员ITS203_autojs运行久了崩溃

autojs内存处理机制代码,避免经常崩溃 群里有人问:为啥auto js运行脚本经常崩溃啊?各位大佬有解决办法不有没有什么内存处理机制,经常崩溃了当然是有的.其实这个原因是autojs本身的线程代码threads.start(action)有内存泄露.我们用Java的即可let obj = { run: function () { print("开始任务") console.show() console.setTitle("运行日志") .

随便推点

自动化测试心路历程(转载)_御风Daniel的博客-程序员ITS203

回想自动化测试的学习过程,因为发现很多人总是急于求成,不懂该如何学习。当前是个新手的时候肯定会选择加很多的群。在群里总是会遇到很多人问低级问题,写了一个selenium脚本,却执行失败,也不看报错,就直接跑到群里来问(有时候连个图都不发或发图只截图一小半,有的甚至问我和你写的代码一样怎么运行不起来了呢?),大神,这里为什么会报错?很多都是明显的语法问题,一问才知道,今天刚刚学selenium,之前也没有语法基础,没学过Java,没学过Python,就跑来搞selenium,想玩自动化。每当看到这样的同学,总

非结构化数据解决方案:优化、优化、优化_小信瑞的博客-程序员ITS203

非结构化数据解决方案:优化、优化、优化无论是管理分布式内容,交付富媒体,还是利用物联网 (IoT),NetApp 都可以帮助您在非结构化数据的整个生命周期内灵活地优化非结构化数据。为什么选择 NetApp 的非结构化数据解决方案?行业领先的非结构化数据管理针对非结构化数据的 NetApp 解决方案可提供极具竞争力的存储服务,帮助您在数据的整个生命周期内优化数据的价值。其中包括与公有云和私有云服务集成,自动将数据放置在最佳存储层,以及为服务提供商和企业提供一个平台来提供全球分布式数据存储库。NetA

Java如何从方法返回多个值_xiamin的博客-程序员ITS203_java返回两个值的方法

讲课时遇到Java如何从方法返回多个值问题,本文介绍三个方法,使Java方法返回多个值。方法1:使用集合类方法2:使用封装对象方法3:使用引用传递示例代码如下:import java.util.HashMap;import java.util.Map;public class Test { /** * 方法1:使用集合类 (Map以外的集合类也可以随意使用)...

vba抓取html文件数据,VBA抓取PDF数据_少读红楼的博客-程序员ITS203

Excel VBA如何批量获取PDF文件中的内容VBA能不能在pdf文件的内容中提取所需要的内容建议把人工操作步骤,写成按键精灵程序去半自动化完成。vba对pdf处理极弱,python也好不了太多。或者你就把pdf转化成office文件(工具很多,例如Acrobat)再用vba处理。如何利用excel VBA生成PDF文件首先,加载引用,如图 然后,在打印机中对Acrobat Distiller的...

接口测试-工作心得记录十一(unittest.skipif使用)_gogoboi_jin的博客-程序员ITS203

背景:最近把jenkins的调用频率调高了,这样就遇到之前没有遇到的问题,接口不同调用频率对应response返回json结构体会有不同。举个例子,商户端都有发帖的功能,postpub发布接口一天只能发布10个帖子,当超过10个的时候就会有不同的json结构体。面对这样的问题,解决方法我想到就2个,第一就是在书写case的时候写两个hope_response,在check接口返回response的

BLE的自动连接_运动图像流的博客-程序员ITS203_ble自动连接

已BLE HID为例,Android原生代码bta_hh_le_add_dev_bg_conn按照Spec描述,在加载配对记录后,对已配的BLE HID Device设备执行了Auto connection流程,代码内,会触发中心设备向BLE HID Device设备自动发起连接的情况包括如下三种:配对成功之后; 车机蓝牙(中心设备)启动,加载配对记录之后; 原因是connection t...

推荐文章

热门文章

相关标签