1. 程式人生 > >程式碼控制Quartz的啟動和停止

程式碼控制Quartz的啟動和停止

Spring中如何使用Quartz就不必說了,這裡說說如果動態設定cron。
這個要解決3個問題:
1.將使用者輸入轉換為Cron表示式
2.安全的重啟quartz
3.程式啟動的時候,從資料庫中讀取cron(而非spring配置檔案中寫死的那個)。
因為derpvail急著用,所以先說第3個。
動態讀取資料庫中的Cron,作為CronTriggerBean的屬性
從資料庫中讀取cron,需要做一個CronExpressionFactoryBean,它是一個Spring的FactoryBean,可以讀取資料庫,並建立一個CronExpression物件:
public class CronFactoryBean implements FactoryBean {
  private static Logger logger = LoggerFactory.getLogger(CronFactoryBean.class);
  @Autowired
  @Qualifier("defaultFreq")
  private String defaultCron;
  /**
   * 用於取得CRON表示式.
   */
  @Autowired(required = true)
  private CronManager cronManager;
  
  
  /**
   * 從資料庫中取得CRON表示式,如果資料庫中沒有則取預設值.
   * @see EmailConstants#DEFALUT_CRON
   */
  @Override
  public Object getObject() throws Exception {
    //如何讀取資料庫,就省了
    String cronEx = cronManager.getCronExpression();
    if(StringUtils.isBlank(cronEx)) {
      return new CronExpression(defaultCron);
    }
    return new CronExpression(cronEx.trim());
  }

  @SuppressWarnings("unchecked")
  @Override
  public Class getObjectType() {
    return CronExpression.class;
  }

  @Override
  public boolean isSingleton() {
    return true;
  }

}


然後呢,就是吧這個CronExpression物件注入到CronTriggerBean,但是CronTriggerBean的setCronExpression方法是過載的,本人不知道如何注入過載方法,所以只好繼承了CronTriggerBean,添加了一個setCron方法,一遍於Spring的注入:
/**
 * FIXME:因為目前不知道如何實現過載方法的注入,而<code>CronTrigger</code>
 * 的<code>setCronExpression</code>既可以用<code>CronExpression</code>
 * 物件作為引數也可以用String作為引數,這就產生了不確定性。所以,我們extends了
 * <code>CronTriggerBean</code>,提供{@link #setCron(CronExpression)}
 * 方法,避免這種不確定性。
 * @author Sam
 *
 */
public class CronTriggerBeanEx extends CronTriggerBean {
  /**
   * 呼叫父類的{@link CronTriggerBean#setCronExpression(CronExpression)}
   * 方法.
   */
  public void setCron(CronExpression cronExpression) {
    this.setCronExpression(cronExpression);
  }
}


然後就是在spring中配置這兩個Bean了,和普通的quartz類似,只不過cronExpression屬性改為了cron屬性:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans default-lazy-init="false">
	<bean id="seismicEmailCronTrigger"
		class="datashare.email.admin.jobs.CronTriggerBeanEx">		
		<property name="jobDetail" ref="seismicEmailJobDetail" />
		<property name="cron">
			<bean class="datashare.email.admin.jobs.CronFactoryBean">
			  </property>
			</bean>
		</property>
	</bean>
</beans>

注意default-lazy-init="false",這樣,quartz就可以在專案啟動的時候,讀取資料庫中的cron了。

安全的重啟quartz
貌似網上的文章都不太對,下面的這方法可以正常執行,在此之前,你應該把ApplicationContext注入,可以用ApplicationContextAware介面,也可以用@Autowired註解:

@Autowired(required = true)
  private ApplicationContext ctx;

/**
   * 如果cron改變,則重新啟動Quartz任務。
   
   * @param signCron 使用者輸入的CRON,你可以把它先存入資料庫
   * @throws ApplicationException 如果CRON無法解析,或原來的任務無法關閉,以及無法啟動新任務.
   */
  private void restartJobs(String signCron) {
    if(StringUtils.isBlank(signCron)) {
      logger.warn("CRON未設定。");
      return;
    }
    //trim一下,難保沒有空格
    signCron = signCron.trim();
    //得到trigger
    
    CronTrigger signCronTrigger = (CronTrigger) ctx.getBean("signEmailCronTrigger", CronTrigger.class);
    //如果頻率都有變,則不必重新啟動.
    if(signCron.equals(signCronTrigger.getCronExpression())) {
      logger.info("前兆和測震傳送頻率都未改變,Quartz不必重新啟動.");
      return;
    }
    //[b]下面是關鍵[/b]
    //得到SchedulerFactoryBean的例項,注意beanName前面的&符號
    SchedulerFactoryBean schedulerFactory = (SchedulerFactoryBean) ctx.getBean("&emailSchedulerFactory");
    try {
      //重新設定trigger
      signCronTrigger.setCronExpression(signCron);
      schedulerFactory.destroy(); //關閉原來的任務
      schedulerFactory.afterPropertiesSet(); //啟動新的任務
      logger.info("XXX任務啟動成功.");
    } catch (ParseException e) {
      throw new ApplicationException("Cron表示式解析錯誤." + e.getMessage());
    } catch (SchedulerException e) {
      e.printStackTrace();
      throw new ApplicationException("關閉定時任務出現異常.");
    } catch (Exception e) {
      e.printStackTrace();
      throw new ApplicationException("啟動定時任務出現異常.");
    }    
  }
//我原來的程式碼和這個略有不同,這段程式碼沒有除錯過(在JE上編輯的),不過應該可以執行


文章轉載  http://cats-tiger.iteye.com/blog/440687