1. 程式人生 > >Quartz整合springMVC 的方案二(持久化任務、叢集和分散式)

Quartz整合springMVC 的方案二(持久化任務、叢集和分散式)

Quartz是一個開放原始碼專案,專注於任務排程器,提供了極為廣泛的特性如持久化任務,叢集和分散式任務等。 Quartz核心是排程器,還採用多執行緒管理。

1.持久化任務:當應用程式停止執行時,所有排程資訊不被丟失,當你重新啟動時,排程資訊還存在,這就是持久化任務。

2.叢集和分散式處理:當在叢集環境下,當有配置Quartz的多個客戶端時(節點),採用Quartz的叢集和分散式處理時,我們要了解幾點好處 1) 一個節點無法完成的任務,會被叢集中擁有相同的任務的節點取代執行。2) Quartz排程是通過觸發器的類別來識別不同的任務,在不同的節點定義相同的觸發器的類別,這樣在叢集下能穩定的執行,一個節點無法完成的任務,會被叢集中擁有相同的任務的節點取代執行。3

分散式 體現在 當相同的任務定時在一個時間點,在那個時間點,不會被兩個節點同時執行。

 Quartz的 Task11 張表)例項化採用資料庫儲存,基於資料庫引擎及 High-Available 的策略(叢集的一種策略)自動協調每個節點的 Quartz

 
delete from qrtz_fired_triggers;
delete from qrtz_simple_triggers;
delete from qrtz_simprop_triggers;
delete from qrtz_cron_triggers;
delete from qrtz_blob_triggers;
delete from qrtz_triggers;
delete from qrtz_job_details;
delete from qrtz_calendars;
delete from qrtz_paused_trigger_grps;
delete from qrtz_locks;
delete from qrtz_scheduler_state;
 
 
 
CREATE TABLE qrtz_job_details
  (
    SCHED_NAME VARCHAR2(120) NOT NULL,
    JOB_NAME  VARCHAR2(200) NOT NULL,
    JOB_GROUP VARCHAR2(200) NOT NULL,
    DESCRIPTION VARCHAR2(250) NULL,
    JOB_CLASS_NAME   VARCHAR2(250) NOT NULL, 
    IS_DURABLE VARCHAR2(1) NOT NULL,
    IS_NONCONCURRENT VARCHAR2(1) NOT NULL,
    IS_UPDATE_DATA VARCHAR2(1) NOT NULL,
    REQUESTS_RECOVERY VARCHAR2(1) NOT NULL,
    JOB_DATA BLOB NULL,
    CONSTRAINT QRTZ_JOB_DETAILS_PK PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);
CREATE TABLE qrtz_triggers
  (
    SCHED_NAME VARCHAR2(120) NOT NULL,
    TRIGGER_NAME VARCHAR2(200) NOT NULL,
    TRIGGER_GROUP VARCHAR2(200) NOT NULL,
    JOB_NAME  VARCHAR2(200) NOT NULL, 
    JOB_GROUP VARCHAR2(200) NOT NULL,
    DESCRIPTION VARCHAR2(250) NULL,
    NEXT_FIRE_TIME NUMBER(13) NULL,
    PREV_FIRE_TIME NUMBER(13) NULL,
    PRIORITY NUMBER(13) NULL,
    TRIGGER_STATE VARCHAR2(16) NOT NULL,
    TRIGGER_TYPE VARCHAR2(8) NOT NULL,
    START_TIME NUMBER(13) NOT NULL,
    END_TIME NUMBER(13) NULL,
    CALENDAR_NAME VARCHAR2(200) NULL,
    MISFIRE_INSTR NUMBER(2) NULL,
    JOB_DATA BLOB NULL,
    CONSTRAINT QRTZ_TRIGGERS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    CONSTRAINT QRTZ_TRIGGER_TO_JOBS_FK FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) 
      REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP) 
);
CREATE TABLE qrtz_simple_triggers
  (
    SCHED_NAME VARCHAR2(120) NOT NULL,
    TRIGGER_NAME VARCHAR2(200) NOT NULL,
    TRIGGER_GROUP VARCHAR2(200) NOT NULL,
    REPEAT_COUNT NUMBER(7) NOT NULL,
    REPEAT_INTERVAL NUMBER(12) NOT NULL,
    TIMES_TRIGGERED NUMBER(10) NOT NULL,
    CONSTRAINT QRTZ_SIMPLE_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    CONSTRAINT QRTZ_SIMPLE_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_cron_triggers
  (
    SCHED_NAME VARCHAR2(120) NOT NULL,
    TRIGGER_NAME VARCHAR2(200) NOT NULL,
    TRIGGER_GROUP VARCHAR2(200) NOT NULL,
    CRON_EXPRESSION VARCHAR2(120) NOT NULL,
    TIME_ZONE_ID VARCHAR2(80),
    CONSTRAINT QRTZ_CRON_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    CONSTRAINT QRTZ_CRON_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 
      REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_simprop_triggers
  (          
    SCHED_NAME VARCHAR2(120) NOT NULL,
    TRIGGER_NAME VARCHAR2(200) NOT NULL,
    TRIGGER_GROUP VARCHAR2(200) NOT NULL,
    STR_PROP_1 VARCHAR2(512) NULL,
    STR_PROP_2 VARCHAR2(512) NULL,
    STR_PROP_3 VARCHAR2(512) NULL,
    INT_PROP_1 NUMBER(10) NULL,
    INT_PROP_2 NUMBER(10) NULL,
    LONG_PROP_1 NUMBER(13) NULL,
    LONG_PROP_2 NUMBER(13) NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR2(1) NULL,
    BOOL_PROP_2 VARCHAR2(1) NULL,
    CONSTRAINT QRTZ_SIMPROP_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    CONSTRAINT QRTZ_SIMPROP_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 
      REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_blob_triggers
  (
    SCHED_NAME VARCHAR2(120) NOT NULL,
    TRIGGER_NAME VARCHAR2(200) NOT NULL,
    TRIGGER_GROUP VARCHAR2(200) NOT NULL,
    BLOB_DATA BLOB NULL,
    CONSTRAINT QRTZ_BLOB_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    CONSTRAINT QRTZ_BLOB_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_calendars
  (
    SCHED_NAME VARCHAR2(120) NOT NULL,
    CALENDAR_NAME  VARCHAR2(200) NOT NULL, 
    CALENDAR BLOB NOT NULL,
    CONSTRAINT QRTZ_CALENDARS_PK PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);
CREATE TABLE qrtz_paused_trigger_grps
  (
    SCHED_NAME VARCHAR2(120) NOT NULL,
    TRIGGER_GROUP  VARCHAR2(200) NOT NULL, 
    CONSTRAINT QRTZ_PAUSED_TRIG_GRPS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_fired_triggers 
  (
    SCHED_NAME VARCHAR2(120) NOT NULL,
    ENTRY_ID VARCHAR2(95) NOT NULL,
    TRIGGER_NAME VARCHAR2(200) NOT NULL,
    TRIGGER_GROUP VARCHAR2(200) NOT NULL,
    INSTANCE_NAME VARCHAR2(200) NOT NULL,
    FIRED_TIME NUMBER(13) NOT NULL,
    SCHED_TIME NUMBER(13) NOT NULL,
    PRIORITY NUMBER(13) NOT NULL,
    STATE VARCHAR2(16) NOT NULL,
    JOB_NAME VARCHAR2(200) NULL,
    JOB_GROUP VARCHAR2(200) NULL,
    IS_NONCONCURRENT VARCHAR2(1) NULL,
    REQUESTS_RECOVERY VARCHAR2(1) NULL,
    CONSTRAINT QRTZ_FIRED_TRIGGER_PK PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);
CREATE TABLE qrtz_scheduler_state 
  (
    SCHED_NAME VARCHAR2(120) NOT NULL,
    INSTANCE_NAME VARCHAR2(200) NOT NULL,
    LAST_CHECKIN_TIME NUMBER(13) NOT NULL,
    CHECKIN_INTERVAL NUMBER(13) NOT NULL,
    CONSTRAINT QRTZ_SCHEDULER_STATE_PK PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);
CREATE TABLE qrtz_locks
  (
    SCHED_NAME VARCHAR2(120) NOT NULL,
    LOCK_NAME  VARCHAR2(40) NOT NULL, 
    CONSTRAINT QRTZ_LOCKS_PK PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);
 
create index idx_qrtz_j_req_recovery on qrtz_job_details(SCHED_NAME,REQUESTS_RECOVERY);
create index idx_qrtz_j_grp on qrtz_job_details(SCHED_NAME,JOB_GROUP);
 
create index idx_qrtz_t_j on qrtz_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP);
create index idx_qrtz_t_jg on qrtz_triggers(SCHED_NAME,JOB_GROUP);
create index idx_qrtz_t_c on qrtz_triggers(SCHED_NAME,CALENDAR_NAME);
create index idx_qrtz_t_g on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP);
create index idx_qrtz_t_state on qrtz_triggers(SCHED_NAME,TRIGGER_STATE);
create index idx_qrtz_t_n_state on qrtz_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
create index idx_qrtz_t_n_g_state on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
create index idx_qrtz_t_next_fire_time on qrtz_triggers(SCHED_NAME,NEXT_FIRE_TIME);
create index idx_qrtz_t_nft_st on qrtz_triggers(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
create index idx_qrtz_t_nft_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
create index idx_qrtz_t_nft_st_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
create index idx_qrtz_t_nft_st_misfire_grp on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
 
create index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME);
create index idx_qrtz_ft_inst_job_req_rcvry on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
create index idx_qrtz_ft_j_g on qrtz_fired_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP);
create index idx_qrtz_ft_jg on qrtz_fired_triggers(SCHED_NAME,JOB_GROUP);
create index idx_qrtz_ft_t_g on qrtz_fired_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
create index idx_qrtz_ft_tg on qrtz_fired_triggers(SCHED_NAME,TRIGGER_GROUP);

我在新建一個張表,為了方便頁面能對每個任務進行管理,能對具體某個任務設定開始時間、結束時間、執行的方法、刪除等, 如下面圖所示:


在這邊可以管理開始時間和結束時間和cronExpression值,方便管理對應表的設計:


表都設計好了,整理Quartz整合springMVC的具體的實現。

Spring@component 的說明:@component (把普通pojo例項化到spring容器中,相當於配置檔案中的<bean id="" class=""/>

1.舉例個任務具體實現功能,列如quartzJobA和quartzJobB任務要做什麼,新建了兩個類和方法。

import org.springframework.stereotype.Component;

@Component("quartzJobA")
public class Data2ServiceImpl {

	public void run() {
		System.out.println("=============Data2ServiceImpl=========");
		
	}
}

@Component("quartzJobB")
public class DataServiceImpl {

  
	public void test() {
		System.out.println("=============DataServiceImpl=========");
		
	}

}

2.Quartz 排程任務所需的配置檔案 quartz-job.properties

 
#Main Scheduler Settings
org.quartz.scheduler.instanceName=quartzScheduler  
org.quartz.scheduler.instanceId=AUTO
org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer=true
org.quartz.scheduler.skipUpdateCheck=true
org.quartz.scheduler.batchTriggerAcquisitionMaxCount=100

org.quartz.threadPool.threadCount=10

#Configure JDBC-JobStoreTX
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.dataSource=myDS
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.acquireTriggersWithinLock=true
org.quartz.jobStore.clusterCheckinInterval = 30000

#Configure DataSources
org.quartz.dataSource.myDS.driver=com.alibaba.druid.proxy.DruidDriver
org.quartz.dataSource.myDS.URL=jdbc:wrap-jdbc:filters=default:name=dragoon:jdbc:oracle:thin:@127.0.0.1:1521:test
org.quartz.dataSource.myDS.user=
org.quartz.dataSource.myDS.password=
org.quartz.dataSource.myDS.maxConnections=5
org.quartz.dataSource.myDS.validationQuery=select 1 from dual
org.quartz.scheduler.instanceName 屬性可為任何值,用在 JDBC JobStore 中來唯一標識例項,但是所有叢集節點中必須相同 

org.quartz.jobStore.class屬性為 JobStoreTX,將任務持久化到資料中。因為叢集中節點依賴於資料庫來傳播 Scheduler 例項的狀態,你只能在使用 JDBC JobStore 時應用 Quartz 叢集。這意味著你必須使用 JobStoreTX 或是 JobStoreCMT 作為 Job 儲存;你不能在叢集中使用 RAMJobStore

3.實現任務的建立和管理

@Component("schedulerHelper")
public class SchedulerHelper
{

	private static final String CONFIG_FILE="quartz-job.properties";
	private static final String IDENTITY_JOB_PREFIX="job_";
	private static final String IDENTITY_TRIGGER_PREFIX="trigger_";
        
       @Autowired
    private JobService jobService;//jobService 這個服務是實現管理任務的頁面的服務實現
	private Scheduler scheduler;
	
	

	@Autowired
	private StartJobSchedulerListener startJobSchedulerListener;//實現自己的Scheduler監聽器,程式啟動時,任務沒建立時就建立

	
	
	
       /**
	 * tomcat一啟動時,類例項化時就執行
	 */
	public void  init()
	{
		try{
		     
             // 建立一個定時器工廠
			StdSchedulerFactory sf = new StdSchedulerFactory();
                        //初始化quartz-job.properties配置檔案
			sf.initialize(Thread.currentThread().getContextClassLoader().getResource(CONFIG_FILE).getFile());
			scheduler = sf.getScheduler();
			//把jobService放到scheduler上下文,job執行是可以獲取並訪問。
			scheduler.getContext().put(SCHEDULER_KEY_JOBSERVICE,jobService);
			startJobSchedulerListener.setSchedulerHelper(this);
                         //設定自己的監聽器
			scheduler.getListenerManager().addSchedulerListener(startJobSchedulerListener);
                       // 啟動定時器
			scheduler.start();
			logger.info("====================job scheduler start");			
		}catch(SchedulerException e){
			logger.error("error",e);			
		}
		
	}
	
        
         /**
	 * 根據jobentity建立並開始任務
	 */
   public boolean createAndStartJob(JobEntity job)
	{
		JobDetail jobDetail=generateJobDetail(job);
		Trigger trigger=generateTriggerBuilder(job).build();
		
		try {
			scheduler.scheduleJob(jobDetail, trigger);
			return true;
		} catch (SchedulerException e) {
			logger.error("scheduler.scheduleJob",e);
			return false;
		}
	}
        /**
	 * 清除
	 */
	public void clearAllScheduler()
	{
		try {
			scheduler.clear();
		} catch (SchedulerException e) {
			logger.error("clearAllScheduler",e);
		}
	}
	
        
         /**
	 * 根據jobId和型別刪除
	 */
	public boolean removeJob(Long jobId,String jobType) 
	{
		try {
			scheduler.deleteJob(getJobKey(jobId,jobType));
			return true;
		} catch (SchedulerException e) {
			logger.error("removeJob",e);
			return false;
		}		
	}
	    
         /**
	 * 暫停任務
	 */
	public boolean pauseJob(Long jobId,String jobType)
	{
		try {
			scheduler.pauseJob(getJobKey(jobId,jobType));
			return true;
		} catch (SchedulerException e) {
			logger.error("resumeJob",e);
			return false;
		}
	}
	 
        /**
	 * 馬上只執行一次任務
	 */
	public boolean executeOneceJob(Long jobId,String jobType)
	{
		try {
			Calendar end=Calendar.getInstance();			
			TriggerBuilder<SimpleTrigger> simpleTriggerBuilder=TriggerBuilder.newTrigger()
												.withIdentity(getTriggerKey(jobId,jobType))
												.forJob(getJobKey(jobId,jobType))
												.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2));
			end.add(Calendar.SECOND, 2);
			simpleTriggerBuilder.startAt(end.getTime());
			end.add(Calendar.SECOND, 5);
			simpleTriggerBuilder.endAt(end.getTime());
			JobEntity job=jobService.getJobById(jobId);
			
			JobDataMap jobDataMap=new JobDataMap();
			jobDataMap.put("jobEntity", job);
			simpleTriggerBuilder.usingJobData(jobDataMap);
			Trigger trigger=simpleTriggerBuilder.build();
			
			scheduler.scheduleJob(trigger);
			return true;
		} catch (SchedulerException e) {
			logger.error("executeOneceJob",e);
			return false;
		}
	}
        /**
	 * 啟動一些scheduler裡沒有的active的jobDetail
	 */
	public void createActiveJobFromDB() throws SchedulerException
	{
		List<JobEntity> jobs=jobService.getActiveJob();
		for(JobEntity job:jobs)
		{
			if(scheduler.getJobDetail(getJobKey(job))==null)
				createAndStartJob(job);
		}
	}
        
       /**
	 * 獲得任務的jobKey
	 */
	public static JobKey getJobKey(Long jobId,String jobType)
	{
		return new JobKey(IDENTITY_JOB_PREFIX+jobId,IDENTITY_JOB_PREFIX+jobType);
	}
	
      /**
	 * 獲得任務的jobKey
	 */

	public static JobKey getJobKey(JobEntity job)
	{
		return new JobKey(IDENTITY_JOB_PREFIX+job.getJobId(),IDENTITY_JOB_PREFIX+job.getJobType());
	}
	
    /**
	 * 獲得trigger的triggerkey
	 */
	public static TriggerKey getTriggerKey(JobEntity job)
	{
		return new TriggerKey(IDENTITY_TRIGGER_PREFIX+job.getJobId()+"_"+System.currentTimeMillis(), IDENTITY_TRIGGER_PREFIX+job.getJobType());
	}
	

        /**
	 * 獲得trigger的triggerkey
	 */
	public static TriggerKey getTriggerKey(Long jobId,String jobType)
	{
		return new TriggerKey(IDENTITY_TRIGGER_PREFIX+jobId+"_"+System.currentTimeMillis(), IDENTITY_TRIGGER_PREFIX+jobType);
	}
	
	public static JobDetail generateJobDetail(JobEntity job)
	{
		JobDataMap jobDataMap=new JobDataMap();
		jobDataMap.put("jobEntity", job);
		Class<? extends Job> clazz=null;
		 clazz=BeanJob.class;
		return JobBuilder.newJob(clazz)
			    		.withIdentity(getJobKey(job))
			    		.usingJobData(jobDataMap)
			    		.requestRecovery(true).storeDurably(true)
			    		.build();
	}
	
	
       /**
	 * 根據jobEntity獲得trigger
	 */

	public static TriggerBuilder<CronTrigger> generateTriggerBuilder(JobEntity job)
	{
		TriggerBuilder<CronTrigger> triggerBuilder= TriggerBuilder.newTrigger()
	    		.withIdentity(getTriggerKey(job))
				.withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpr())
												.withMisfireHandlingInstructionDoNothing());
		if(job.getSyncBeginTime()!=null)
			triggerBuilder.startAt(job.getSyncBeginTime());
		else
			triggerBuilder.startNow();
		
		if(job.getSyncEndTime()!=null)
			triggerBuilder.endAt(job.getSyncEndTime());
	
		return triggerBuilder;
	}
	
	public static JobService getJobService(JobExecutionContext context)
	{
		try {
			return (JobService) context.getScheduler().getContext().get(SchedulerHelper.SCHEDULER_KEY_JOBSERVICE);
		} catch (SchedulerException e) {
			logger.error("SchedulerHelper.getJobService",e);
			return null;
		}
	}
	

4.實現自己的Scheduler監聽器,程式啟動時,建立scheduler裡沒有的activejobDetail

@Component(value="startJobSchedulerListener")
public class StartJobSchedulerListener extends SchedulerListenerSupport 
{
	private SchedulerHelper schedulerHelper;
	
	@Override
	public void schedulerStarted()
	{
		try {
			schedulerHelper.createActiveJobFromDB();
		} catch (SchedulerException e) {
			logger.error("createActiveJobFromDB",e);
		}
	}

	public SchedulerHelper getSchedulerHelper() {
		return schedulerHelper;
	}

	public void setSchedulerHelper(SchedulerHelper schedulerHelper) {
		this.schedulerHelper = schedulerHelper;
	}

			
}
5.實現的是一個job例項對應一個執行緒並實現頁面配置對應的哪個類和方法   
public abstract class AbstractEdiJob implements Job 
{
	protected JobEntity jobEntity; 
	protected static final Logger logger=LoggerFactory.getLogger(AbstractEdiJob.class);
	private Long beginTime;
		
	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException 
	{
		JobService jobService=SchedulerHelper.getJobService(context);
		preExcute(jobService,context);
		exeucuteInternal(context);
		postExcute(jobService,context);
	}
	
	abstract public void exeucuteInternal(JobExecutionContext context);
	
	public void preExcute(JobService jobService,JobExecutionContext context)
	{
		beginTime=System.currentTimeMillis();
	}
	
	public void postExcute(JobService jobService,JobExecutionContext context)
	{
		//獲得最新的jobEntiry
		jobEntity=jobService.getJobById(jobEntity.getJobId());
		if(jobEntity==null)
		{
			logger.warn(jobEntity.getJobId()+"job不能存在");
			return;
		}
		if(context.getFireTime()!=null)
			jobEntity.setRuntimeLast(context.getFireTime());
		if(context.getNextFireTime()!=null)
			jobEntity.setRuntimeNext(context.getNextFireTime());
	/*	else
			jobEntity.setJobStatus();*/
		Long times=jobEntity.getRunTimes();
		jobEntity.setRunTimes((times==null?0l:times)+1);
		Long duration=jobEntity.getRunDuration();
		jobEntity.setRunDuration((duration==null?0l:duration)+(System.currentTimeMillis()-beginTime));
		jobService.updateJob(jobEntity);
		//jobEntity這裡的改變不能改變JobDetail裡的JobEntity,因為生產的job是JobDetail的JobEntity的複製
	}


	public void setJobEntity(JobEntity jobEntity) {
		this.jobEntity = jobEntity;
	}


}
/**
 *執行具體類中的方法
 **/
public class BeanJob extends AbstractEdiJob 
{
	private static Logger logger=LoggerFactory.getLogger(BeanJob.class);
	@Override
	public void exeucuteInternal(JobExecutionContext context) 
	{
		Object obj=SpringContextUtil.getBean(jobEntity.getJobObject());
		try {
			Method method=obj.getClass().getMethod(jobEntity.getJobMethod());
			method.invoke(obj);
		} catch (SecurityException e) {
			logger.error("error",e);
		} catch (NoSuchMethodException e) {
			logger.error("error",e);
		} catch (IllegalArgumentException e) {
			logger.error("error",e);
		} catch (IllegalAccessException e) {
			logger.error("error",e);
		} catch (InvocationTargetException e) {
			logger.error("error",e);
		}
	}

}


6.新增一個任務時,資料庫就儲存對應的觸發器,變成持久化任務,如圖所示:

  


    1.用StdSchedulerFactory來獲取Scheduler的例項,scheduler有啟動(start)、中止(stop)和暫停(pause)方法。
    2.JobDataMap例項,JobDataMap jobDataMap=new JobDataMap();jobDataMap.put("jobEntity", job);在同一任務的多次執行之間傳遞資料
    3.建立JobDetail例項。JobBuilder.newJob(clazz).withIdentity(getJobKey(job)).usingJobData(jobDataMap).requestRecovery(true).storeDurably(true).build();返回JobDetail例項,newJob(clazz)是要執行特定任務的類;withIdentity(getJobKey(job))是job的任務名和組名;usingJobDatausingJobData(jobDataMap)傳輸資料;


    4.建立Trigger例項。TriggerBuilder<CronTrigger> triggerBuilder= TriggerBuilder.newTrigger()
                                                                                                .withIdentity(getTriggerKey(job))
                                                                                     .withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpr())
                      .withMisfireHandlingInstructionDoNothing());

   withIdentity有標示了觸發器的名稱和組(Quartz排程是通過觸發器的類別來識別不同的任務),和withSchedule標示執行的時間表達式


    5.最後通過scheduler.scheduleJob()方法排程任務。