Quartz在Spring中动态设置cronExpression

来源:互联网 发布:windows无法格式化sd卡 编辑:程序博客网 时间:2024/05/04 13:05

版权所有:(xiaodaoxiaodao)蓝小刀    xiaodaoxiaodao@gmail.com

http://www.blogjava.net/xiaodaoxiaodao/articles/103437.html       

转载请注明来源/作者

 

Quartz在Spring中动态设置cronExpression

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

这样总不能修改配置文件每定制个定时任务就增加一个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>

      中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 originCronExpression = 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必须extendsCronTriggerBean.

 

public class InitializingCronTrigger extendsCronTriggerBean implements Serializable {

    private ScheduleInfoManager scheduleInfoManager;

    // 设值注入,通过setter方法传入被调用者的实例scheduleInfoManager

    public void setScheduleInfoManager(ScheduleInfoManager scheduleInfoManager){

        this.scheduleInfoManager = scheduleInfoManager;

        // 因为在getCronExpressionFromDB使用到了scheduleInfoManager,所以

        // 必须上一行代码设置scheduleInfoManager后进行getCronExpressionFromDB

        String cronExpression = getCronExpressionFromDB ();   //

        // 因为extendsCronTriggerBean,此处调用父类方法初始化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;

     }

    ……

}

  

附表 CronTrigger Expression( 来自http://quartz.sourceforge.net/javadoc/org/quartz/CronTrigger.html)

Expression

Meaning

"0 0 12 * * ?"

Fire at 12pm (noon) every day

"0 15 10 ? * *"

Fire at 10:15am every day

"0 15 10 * * ?"

Fire at 10:15am every day

"0 15 10 * * ? *"

Fire at 10:15am every day

"0 15 10 * * ? 2005"

Fire at 10:15am every day during the year 2005

"0 * 14 * * ?"

Fire every minute starting at 2pm and ending at 2:59pm, every day

"0 0/5 14 * * ?"

Fire every 5 minutes starting at 2pm and ending at 2:55pm, every day

"0 0/5 14,18 * * ?"

Fire every 5 minutes starting at 2pm and ending at 2:55pm, AND fire every 5 minutes starting at 6pm and ending at 6:55pm, every day

"0 0-5 14 * * ?"

Fire every minute starting at 2pm and ending at 2:05pm, every day

"0 10,44 14 ? 3 WED"

Fire at 2:10pm and at 2:44pm every Wednesday in the month of March.

"0 15 10 ? * MON-FRI"

Fire at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday

"0 15 10 15 * ?"

Fire at 10:15am on the 15th day of every month

"0 15 10 L * ?"

Fire at 10:15am on the last day of every month

"0 15 10 ? * 6L"

Fire at 10:15am on the last Friday of every month

"0 15 10 ? * 6L"

Fire at 10:15am on the last Friday of every month

"0 15 10 ? * 6L 2002-2005"

Fire at 10:15am on every last friday of every month during the years 2002, 2003, 2004 and 2005

"0 15 10 ? * 6#3"

Fire at 10:15am on the third Friday of every month

 

版权所有:(xiaodaoxiaodao)蓝小刀    xiaodaoxiaodao@gmail.com

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

参靠上述文章跟官方网站,我做出了自己的定时系统。部分相关源代码如下:

public void reScheduleJob() throws SchedulerException, ParseException {
     logger.info("Start to schedule the triggers!");
     List<MailTaskBean> mailTaskList = mailDao.getMailTaskList();
     Map<String, Object> curTriggerList = new HashMap<String, Object>();
     for (int i = 0; i < mailTaskList.size(); i++) {
          MailTaskBean mailTask = mailTaskList.get(i);
          logger.info("Start to deal the task " + mailTask.getMTaskId());
          Date curLastUpdateTime = mailTask.getLastUpdateTime();
          List<MailSchedulingBean> mailSchedulingList = mailDao.getMailSchedulesByTask(mailTask.getMTaskId(), mailTask.getRevision());
         for (int j = 0; j < mailSchedulingList.size(); j++) {
              MailSchedulingBean mailScheduling = mailSchedulingList.get(j);
              logger.info("Start to deal the scheduling " + mailScheduling.getMScheduleId());
              curTriggerList.put("cronTrigger" + mailScheduling.getMScheduleId(), "cronTrigger" + mailScheduling.getMScheduleId());
              CronTriggerBean trigger = (CronTriggerBean)scheduler.getTrigger("cronTrigger" + mailScheduling.getMScheduleId(), Scheduler.DEFAULT_GROUP);
              Date oriLastUpdateTime = new Date();
              if (trigger != null) {
                  oriLastUpdateTime = ((MailTaskBean)trigger.getJobDetail().getJobDataMap().get("mailTask")).getLastUpdateTime();
              }
              if (trigger == null) {
                 JobDetailBean jobDetail = new JobDetailBean();
                  jobDetail.setName("mailJob" + mailScheduling.getMScheduleId());
                  jobDetail.setJobClass(TaskDetail.class);
                  jobDetail.getJobDataMap().put("mailTask", mailTask);
                  jobDetail.getJobDataMap().put("emailTemplateDao", emailTemplateDao);
                  jobDetail.getJobDataMap().put("mailDao", mailDao);
                 CronTriggerBean cronTrigger = new CronTriggerBean();
                 cronTrigger.setName("cronTrigger" + mailScheduling.getMScheduleId());
                 cronTrigger.setJobDetail(jobDetail);
                 cronTrigger.setCronExpression(mailScheduling.getCronExpression());
                 scheduler.scheduleJob(jobDetail, cronTrigger);
              } else if (!trigger.getCronExpression().equals(mailScheduling.getCronExpression()) || lastUpdateTimeChaged(oriLastUpdateTime, curLastUpdateTime)) {
                  JobDetailBean jobDetail = new JobDetailBean();
                   jobDetail.setName("mailJob" + mailScheduling.getMScheduleId());
                   jobDetail.setJobClass(TaskScheduler.class);
                   jobDetail.getJobDataMap().put("mailTask", mailTask);
                   jobDetail.getJobDataMap().put("emailTemplateDao", emailTemplateDao);
                   jobDetail.getJobDataMap().put("mailDao", mailDao);
                   trigger.setCronExpression(mailScheduling.getCronExpression());
                   scheduler.deleteJob("mailJob" + mailScheduling.getMScheduleId(), Scheduler.DEFAULT_GROUP);
                   trigger.setJobDetail(jobDetail);
                   scheduler.rescheduleJob("cronTrigger" + mailScheduling.getMScheduleId(), Scheduler.DEFAULT_GROUP, trigger);
              }
              logger.info("End dealing the scheduling " + mailScheduling.getMScheduleId());
          }
          logger.info("End dealing the task " + mailTask.getMTaskId());
      }
      String[] triggerList = scheduler.getTriggerNames(Scheduler.DEFAULT_GROUP);
       logger.info("In Scheduler trigger total is " + triggerList.length);
       logger.info("Current trigger total is " + curTriggerList.size());
       if (triggerList.length - 1 > curTriggerList.size()) {
           logger.info("Start to delete canceled trigger!");
           for (int k = 0;k < triggerList.length; k++) {
                if (!curTriggerList.containsKey(triggerList[k]) && !"schedulerCronTrigger".equals(triggerList[k])) {
                     logger.info("Deleting the trigger " + triggerList[k]);
                     scheduler.deleteJob(scheduler.getTrigger(triggerList[k], Scheduler.DEFAULT_GROUP).getJobName(), Scheduler.DEFAULT_GROUP);
                     scheduler.unscheduleJob(triggerList[k], Scheduler.DEFAULT_GROUP);
                }
           }
           logger.info("End deleting canceled trigger!");
      }
}

private boolean lastUpdateTimeChaged(Date oriLastUpdateTime, Date curLastUpdateTime) {
      if (oriLastUpdateTime == null && curLastUpdateTime == null) {
          return false;
       } else if (oriLastUpdateTime == null && curLastUpdateTime != null) {
          return true;
       } else if (oriLastUpdateTime.compareTo(curLastUpdateTime) != 0) {
          return true;
       } else {
          return false;
       }
}

public void setScheduler(Scheduler scheduler) {
      this.scheduler = scheduler;
      try {
          TaskSchedulerListener taskSchedulerListener = new TaskSchedulerListener();
           taskSchedulerListener.setMailDao(mailDao);
           this.scheduler.addSchedulerListener(taskSchedulerListener);
      } catch (SchedulerException e) {
          logger.error(e, e);
      }
}

其中TaskScheduler.class见下:

public class TaskDetail extends QuartzJobBean{

    private void sendMail(JobExecutionContext context) {
         JobDataMap dataMap = context.getJobDetail().getJobDataMap();
          MailTaskBean mailTask = (MailTaskBean)dataMap.get("mailTask");
          MailDao mailDao = (MailDao)dataMap.get("mailDao");
          EmailTemplateDao emailTemplateDao = (EmailTemplateDao)dataMap.get("emailTemplateDao");

          ............................

    }

    protected void executeInternal(JobExecutionContext context)
   throws JobExecutionException {
         sendMail(context);
    }

}

0 0
原创粉丝点击