1. 程式人生 > 其它 >spring 整合Quartz動態定時使用

spring 整合Quartz動態定時使用

技術標籤:quartzspring

spring自帶的@Scheduled 定時器只能在後臺固定時間進行處理業務. 無法動態實現定時任務

前段時間專案要求需要從前臺向後臺傳遞執行時間動態執行業務程式碼,從網上找資料瞭解到Quartz 建立job來實現.

Quartz 設計有三個核心類,分別是 Scheduler(排程器)Job(任務)和 Trigger (觸發器),它們是我們使用 Quartz 的關鍵。

2.1 Scheduler介面

Scheduler翻譯成排程器,Quartz通過排程器來註冊、暫停、刪除Trigger和JobDetail。Scheduler還擁有一個SchedulerContext,顧名思義就是上下文,通過SchedulerContext我們可以獲取到觸發器和任務的一些資訊。

2.2 Trigger介面

Trigger可以翻譯成觸發器,通過cron表示式或是SimpleScheduleBuilder等類,指定任務執行的週期。系統時間走到觸發器指定的時間的時候,觸發器就會觸發任務的執行。

2.3 JobDetail介面

Job介面是真正需要執行的任務。JobDetail介面相當於將Job介面包裝了一下,Trigger和Scheduler實際用到的都是JobDetail。

接下來我來回顧一下 實現步驟

  1. 首先將Quartz 的包整合到專案中
    quartz-2.3.0-SNAPSHOT.jar
    quartz-jobs-2.3.0-SNAPSHOT.jar

  2. 建立 job 任務類 繼承quartz包中的job類 重寫 execute方法寫定時任務業務邏輯.
    public class TimingUpdatePrice implements Job {
        //貨源合同列表 service層
        private HtLogisticsOrderDetailService htLogisticsOrderDetailService = SpringContextHolder.getBean(HtLogisticsOrderDetailService.class);
        //貨源單價修改記錄表 service 層
        private HtOrderDetailModificationHistoryService htOrderDetailModificationHistoryService = SpringContextHolder.getBean(HtOrderDetailModificationHistoryService.class);
        private HtTransportDetailService htTransportDetailService = SpringContextHolder.getBean(HtTransportDetailService.class);
    
    
        /**
         * 寫定時任務業務邏輯
         * @param jobExecutionContext
         * @throws JobExecutionException
         */
        @Override
        public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
     //獲取當前物件
            String historyId = jobExecutionContext.getTrigger().getJobKey().toString().replace(QuartzManage.TRIGGER_GROUP_NAME + ".", "");
            HtOrderDetailModificationHistory htOrderDetailModificationHistory = htOrderDetailModificationHistoryService.get(historyId);
            try{
                HtLogisticsOrderDetail htLogisticsOrderDetail = new HtLogisticsOrderDetail();
                if(htOrderDetailModificationHistory !=null){
                    htLogisticsOrderDetail = htLogisticsOrderDetailService.get(htOrderDetailModificationHistory.getOrderDetailId());
                    if("1".equals(htOrderDetailModificationHistory.getPriceType())){//開票運費單價
                        htLogisticsOrderDetail.setFreight(htOrderDetailModificationHistory.getFreight());
                        htLogisticsOrderDetail.setPriceType("1");
                        htLogisticsOrderDetail.setActualPrice(null);
                    }else {//實際運費單價
                        htLogisticsOrderDetail.setActualPrice(htOrderDetailModificationHistory.getActualPrice());//實際運費單價
                        htLogisticsOrderDetail.setPriceType("2");
                        htLogisticsOrderDetail.setFreight(null);
                    }
                    htLogisticsOrderDetail.setId(htOrderDetailModificationHistory.getOrderDetailId());
                }
                htTransportDetailService.save(htLogisticsOrderDetail,htOrderDetailModificationHistory);
    //            htLogisticsOrderDetailService.save(htLogisticsOrderDetail,htOrderDetailModificationHistory);
    //            htOrderDetailModificationHistory.setIsDone("1");
    //            htOrderDetailModificationHistoryService.save(htOrderDetailModificationHistory);
                QuartzManage.removeJob(historyId);
            }catch (Exception e) {
                e.printStackTrace();
            }
    }

  3. 寫排程器類
    public class QuartzManage {
    	
    	
    	private static SchedulerFactory sf = new StdSchedulerFactory();
    	public static final String TRIGGER_GROUP_NAME = "trigger1";
    
    	/**
    	 * 新增一個任務
    	 * 
    	 * @param jobName--任務名稱
    	 * @param job--任務執行物件
    	 * @param time--任務觸發時機
    	 * @throws Exception
    	 */
    	public static void addJob(Job job, String time, String jobName) throws Exception {
    		boolean isValidate = CronExpression.isValidExpression(time);
    		if (!isValidate)
    			return;
    		Scheduler sched = sf.getScheduler();
    		JobKey jobKey = new JobKey(jobName, TRIGGER_GROUP_NAME);
    		JobDetail jobDetail = JobBuilder.newJob(job.getClass()).withIdentity(jobKey).build();
    		Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobName)
    				// 每隔一秒執行 並一直重複
    				.withSchedule(CronScheduleBuilder.cronSchedule(time)).build();
    		sched.scheduleJob(jobDetail, trigger);
    		// 啟動
    		if (!sched.isShutdown()) {
    			sched.start();
    		}
    	}
    
    	/**
    	 * 修改定時任務
    	 * 
    	 * @param jobName
    	 * @param time
    	 * @throws Exception
    	 */
    	public static void modifyJobTime(Job job, String time, String jobName) throws Exception {
    		boolean isValidate = CronExpression.isValidExpression(time);
    		if (!isValidate)
    			return;
    		removeJob(jobName);
    		addJob(job, time, jobName);
    	}
    
    	/**
    	 * 移除任務
    	 * 
    	 * @param jobName
    	 * @throws Exception
    	 */
    	public static void removeJob(String jobName) throws Exception {
    		Scheduler sched = sf.getScheduler();
    		JobKey jobKey = new JobKey(jobName, TRIGGER_GROUP_NAME);
    		sched.pauseJob(jobKey);// 停止觸發器
    		//sched.shutdown(false);// 移除觸發器
    		sched.deleteJob(jobKey);// 刪除任務
    	}
    }

  4. 在業務程式碼中呼叫 排程器

public Map<String,Object> save(HttpServletRequest request,HtOrderDetailModificationHistory htOrderDetailModificationHistory) {
		Map<String,Object> result = Maps.newHashMap();
		try {
			String  message = validateModel(htOrderDetailModificationHistory);
			if (StringUtils.isNotBlank(message)){
				result.put("flag", false);
				result.put("message", message);
				return result;
			}
			//設定通用屬性
			setCommonProperties(request,htOrderDetailModificationHistory);
			htOrderDetailModificationHistory.setIsDone("0");//不能放到service層
			htOrderDetailModificationHistoryService.save(htOrderDetailModificationHistory);

			String cron = CronExpressionUtils.cronGen(htOrderDetailModificationHistory.getPeriodicModificationTime());
			QuartzManage.addJob(new TimingUpdatePrice(), cron, htOrderDetailModificationHistory.getId());
			result.put("flag", true);
			result.put("message", "儲存貨源單價修改記錄成功");
		} catch (Exception e) {
			result.put("flag", false);
			result.put("message", e.toString());
		}
		return result;
	}

5. 若程式重啟了 記憶體中的 job任務就不存在了, 需要放在監聽器中重新將這些任務載入進來.

這個監聽器類 我加web.xml中配置的

<listener>
    <listener-class>com.xiangyou.modules.sys.listener.WebContextListener</listener-class>
  </listener>
public class WebContextListener extends org.springframework.web.context.ContextLoaderListener {
	
	@Override
	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		if (!SystemService.printKeyLoadMessage()){
			return null;
		}
		WebApplicationContext context = super.initWebApplicationContext(servletContext);
		//獲取所有的有時效的繫結資料
		HtOrderDetailModificationHistoryService htOrderDetailModificationHistoryService = context.getBean(HtOrderDetailModificationHistoryService.class);
		try{
			HtOrderDetailModificationHistory htOrderDetailModificationHistory = new HtOrderDetailModificationHistory();
			htOrderDetailModificationHistory.setIsDone("0");
			//迴圈列表,判斷是否過期,如果過期,則直接執行,否則新增入定時任務
			List<HtOrderDetailModificationHistory> list = htOrderDetailModificationHistoryService.findList(htOrderDetailModificationHistory);
			Date modificationTime = null;
			for (HtOrderDetailModificationHistory htOrderDetailModificationHistory1 : list) {
				modificationTime = htOrderDetailModificationHistory1.getPeriodicModificationTime();
				if(modificationTime.before(new Date())){ //已經過期
					String cron = CronExpressionUtils.cronGen(new Date());
					QuartzManage.addJob(new TimingUpdatePrice(), cron, htOrderDetailModificationHistory1.getId());
				}else{
					String cron = CronExpressionUtils.cronGen(modificationTime);
					QuartzManage.addJob(new TimingUpdatePrice(), cron, htOrderDetailModificationHistory1.getId());
				}
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
		return context;
	}
}