Quartz是OpenSymphony開源組織在Job scheduling領域又一個開源項目,它可以與J2EE與J2SE應用程序相結合也可以單獨使用。Quartz可以用來創建簡單或爲運行十個,百個,甚至是好幾萬個Jobs這樣複雜的程序。Jobs可以做成標準的Java組件或 EJBs。Quartz的最新版本爲Quartz 2.2.1。
本次遇到的情況是 關於N多個抓取任務的調度,並且由於各任務本身是有關聯性,不易改造成標準的Jobs,並考慮到不去入侵和修改原來的任務方法,故期望運用Quartz按照Cron設定的時間去執行指定類和方法。
因爲需要保存數據到數據庫,設計了一個JavaBean來存儲數據結構如下:
public class Quartz {
/** 自動編號 */
private Long SCHEDULE_ID;
/** 任務名稱 */
private String NAME;
/** 任務別名 */
private String ALIASNAME;
/** 任務分組 */
private String GROUP;
/** 任務執行類-全路徑 */
private String CLASS;
/** 任務執行類-方法 */
private String METHOD;
/** 任務Cron表達式 */
private String CRON;
/** 任務描述 */
private String DESC;
private int STATUS_ID;
private Long CREATETIME;
private Long UPDATETIME;
private int CREATEUSER;
private int UPDATEUSER;
}
Quartz 將作業劃分爲:執行器(JobDetail)、觸發器(Trigger)、監聽器(Listener),簡略說就是執行器決定如何執行任務,觸發器決定何時執行任務,監聽器決定任務到達某狀態如何操作。
Quartz典型的JobDetail是實現一個Job接口,然後新建一個觸發器去執行該Job。
典型的Quartz應用一:
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class HelloJob implements Job {
private static Logger _log = LoggerFactory.getLogger(HelloJob.class);
public HelloJob() {
}
public void execute(JobExecutionContext context)
throws JobExecutionException {
_log.error(" 執行任務: " + new Date());
}
}
測試類
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
import static org.quartz.DateBuilder.*;
import java.util.Date;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SimpleExample {
private static Logger log = LoggerFactory.getLogger(SimpleExample.class);
public void run() throws Exception {
// 通過SchedulerFactory獲取一個調度器實例
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
Date runTime = evenMinuteDate(new Date());
// 通過過JobDetail封裝HelloJob,同時指定Job在Scheduler中所屬組及名稱,這裏,組名爲group1,而名稱爲job1。
JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1").build();
// 創建一個SimpleTrigger實例,指定該Trigger在Scheduler中所屬組及名稱。
// 接着設置調度的時間規則.當前時間運行
Trigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(runTime).build();
// 註冊並進行調度
sched.scheduleJob(job, trigger);
// 啓動調度器
sched.start();
try {
//當前線程等待65秒
Thread.sleep(65L * 1000L);
} catch (Exception e) {
}
//調度器停止運行
sched.shutdown(true);
log.error("結束運行。。。。");
}
public static void main(String[] args) throws Exception {
SimpleExample example = new SimpleExample();
example.run();
}
}
典型的Quartz應用二:
<!-- 使用MethodInvokingJobDetailFactoryBean,任務類可以不實現Job接口,通過targetMethod指定調用方法-->
<bean id="taskJob" class="com.tyyd.dw.task.DataConversionTask"/>
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="group" value="job_work"/>
<property name="name" value="job_work_name"/>
<!--false表示等上一個任務執行完後再開啓新的任務-->
<property name="concurrent" value="false"/>
<property name="targetObject">
<ref bean="taskJob"/>
</property>
<property name="targetMethod">
<value>execute</value>
</property>
</bean>
<!-- 調度觸發器 -->
<bean id="myTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="name" value="work_default_name"/>
<property name="group" value="work_default"/>
<property name="jobDetail">
<ref bean="jobDetail" />
</property>
<property name="cronExpression">
<value>0/5 * * * * ?</value>
</property>
</bean>
<!-- 調度工廠 -->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="myTrigger"/>
</list>
</property>
</bean>
應用一是常見的java實現方式,沒有使用Spring去管理Quartz對象,也反映Quartz可以脫離Spring獨立執行。應用二是通過Spring配置文件實現執行實例化類的方法,但是把配置寫在XML文件裏面非常不方便修改。因此借鑑第二種方法實現Quartz的動態加載任務,同理也可實現增加、刪除、暫停、啓動、單次執行等功能。
考慮到Quartz調度工廠不需要多例,並且考慮注入的便捷性,在SpringMvc.xml文件裏配置:
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"/>
並將其注入到SchedulerJobService中。
package cn.focus.sh.core.quartz.service;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.stereotype.Service;
@Service
public class ScheduleJobService {
@Autowired
private Scheduler scheduler;
private static Map<String,Quartz> QuartzMap = new ConcurrentHashMap<String,Quartz>();
@Autowired
SpringUtils utils;
@Autowired
private QuartzDao quartzDao;
@PostConstruct
public void init(){
//從數據庫初始化任務列表
List<Quartz> list = quartzDao.selectAll();
for(Quartz quartz :list){
run(quartz);
}
}
public void run(Quartz quartz){
MethodInvokingJobDetailFactoryBean bean =new MethodInvokingJobDetailFactoryBean();
try {
Class<?> loader = Class.forName(quartz.getCLASS());
bean.setTargetObject(utils.getBean(loader));
bean.setTargetClass(loader);
bean.setTargetMethod(quartz.getMETHOD());
bean.setGroup(quartz.getGROUP());
bean.setName(quartz.getNAME());
bean.setConcurrent(false);
bean.afterPropertiesSet();
//表達式調度構建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartz.getCRON());
//按新的cronExpression表達式構建一個新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartz.getNAME(), quartz.getGROUP())
.withSchedule(scheduleBuilder).build();
try {
scheduler.scheduleJob(bean.getObject(), trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
CLASS cn.xxx.xxx.manager.xxxManager
METHOD getCount
CRON 0/10 * * * * ?
目前只實現了動態執行任務,同理可以實現動態暫停、刪除、增加、修改等功能。