1. 程式人生 > >spring實現可動態修改時間定時任務

spring實現可動態修改時間定時任務

            前端時間開發接觸了一個開源框架jeecg,裡面封裝了spring與quartz整合的定時任務實現方式。因為之前嘗試過單純使用quartz來實現定時任務,遇到一些問題,比如,無法通過spring注入的方式新增自己的注入類。

        首先了解一下,定時任務有三種技術實現方式:java自帶的Timer類,可以讓程式保持一定頻度執行,但是無法按照某個時間執行;quartz,一個功能強大的排程器,是由java編寫的作業排程框架,簡單易用;spring3.0之後自帶task,輕量級Quartz。

         梳理實現過程:

       (1)pom檔案引入需要jar包:這裡spring版本為4.0.9.RELEASE,quartz版本為1.6.2

       (2)xml檔案配置執行策略,執行的觸發器,並將觸發器注入到任務排程器中

<!-- 定時任務配置 scheduler 方式  -->
	<context:component-scan base-package="org.jeecgframework.core.timer" />
	<task:executor id="executor" pool-size="5" />
	<task:scheduler id="scheduler" pool-size="10" />
	<task:annotation-driven executor="executor" scheduler="scheduler" />
		
	
	<!-- 定時任務配置   smsSendTask 可配置到管理介面 -->
	<bean id="smsSendTaskJob"
		class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
		<property name="targetObject" ref="smsSendTask" /> //要執行的傳送郵件的方法
		<property name="targetMethod" value="run" />
		<property name="concurrent" value="true" />
	</bean>
	<bean id="smsSendTaskCronTrigger" class="org.jeecgframework.core.timer.DataBaseCronTriggerBean">
		<property name="jobDetail" ref="smsSendTaskJob" />
		<property name="cronExpression" value="0 0/1 * * * ?" />  //每分鐘執行一次
	</bean>
<!-- 定時任務排程器 org.jeecgframework.core.timer.DataBaseSchedulerFactoryBean-->
	<bean id="schedulerFactory" lazy-init="false" autowire="no"
		class="org.jeecgframework.web.system.util.SchedulerFactoryBeanWithShutdownDelay">
		<property name="triggers">
			<list>
			<ref bean="smsSendTaskCronTrigger" />
			</list>
		</property>
	</bean>

      由上面配置可知,定時任務是由spring注入的方式,因此要執行的方法直接按照spring的標準就可以 了。這樣定時任務就算完成了,系統啟動後,會按照執行策略動態執行。

      (3)實現定時任務管理過程:

          (3.1)定義定時間任務實體(描述任務資訊):

@Entity
@Table(name = "t_s_timetask", schema = "")
@DynamicUpdate(true)
@DynamicInsert(true)
@SuppressWarnings("serial")
public class TSTimeTaskEntity implements java.io.Serializable {
	/**id*/
	private java.lang.String id;
	/**任務ID*/
	private java.lang.String taskId;
	/**任務描述*/
	private java.lang.String taskDescribe;
	/**cron表示式*/
	private java.lang.String cronExpression;
	/**是否生效了0未生效,1生效了*/
	private java.lang.String isEffect;
	/**是否執行0停止,1執行*/
	private java.lang.String isStart;
	/**建立時間*/
	private java.util.Date createDate;
	/**建立人ID*/
	private java.lang.String createBy;
	/**建立人名稱*/
	private java.lang.String createName;
	/**修改時間*/
	private java.util.Date updateDate;
	/**修改人ID*/
	private java.lang.String updateBy;
	/**修改人名稱*/
	private java.lang.String updateName;
	/**執行的job名稱*/
	private java.lang.String jobeName;
       {get;set}
}


         (3.2)前臺實現效果:

      

    任務id要與xml任務排程器配置一致。

     (3.3)controller端實現:

@Controller
@RequestMapping("/timeTaskController")
public class TimeTaskController extends BaseController {

	@Autowired
	private TimeTaskServiceI timeTaskService;
	@Autowired
	private DynamicTask dynamicTask;
	@Autowired
	private SystemService systemService;


	/**
	 * 定時任務管理列表 頁面跳轉
	 * 
	 * @return
	 */
	@RequestMapping(params = "timeTask")
	public ModelAndView timeTask(HttpServletRequest request) {
		return new ModelAndView("system/timetask/timeTaskList");
	}

	/**
	 * easyui AJAX請求資料
	 * 
	 * @param request
	 * @param response
	 * @param dataGrid
	 * @param user
	 */

	@RequestMapping(params = "datagrid")
	public void datagrid(TSTimeTaskEntity timeTask,HttpServletRequest request, HttpServletResponse response, DataGrid dataGrid) {
		CriteriaQuery cq = new CriteriaQuery(TSTimeTaskEntity.class, dataGrid);
		//查詢條件組裝器
		org.jeecgframework.core.extend.hqlsearch.HqlGenerateUtil.installHql(cq, timeTask, request.getParameterMap());
		this.timeTaskService.getDataGridReturn(cq, true);
		TagUtil.datagrid(response, dataGrid);
	}

	/**
	 * 刪除定時任務管理
	 * 
	 * @return
	 */
	@RequestMapping(params = "del")
	@ResponseBody
	public AjaxJson del(TSTimeTaskEntity timeTask, HttpServletRequest request) {
		String message = null;
		AjaxJson j = new AjaxJson();
		timeTask = systemService.getEntity(TSTimeTaskEntity.class, timeTask.getId());
		message = "定時任務管理刪除成功";
		timeTaskService.delete(timeTask);
		systemService.addLog(message, Globals.Log_Type_DEL, Globals.Log_Leavel_INFO);
		j.setMsg(message);
		return j;
	}


	/**
	 * 新增定時任務管理
	 * 
	 * @param ids
	 * @return
	 */
	@RequestMapping(params = "save")
	@ResponseBody
	public AjaxJson save(TSTimeTaskEntity timeTask, HttpServletRequest request) {
		String message = null;
		AjaxJson j = new AjaxJson();
		CronTrigger trigger = new CronTrigger();
		try {
			trigger.setCronExpression(timeTask.getCronExpression());
		} catch (ParseException e) {
			j.setMsg("Cron表示式錯誤");
			return j;
		}
		if (StringUtil.isNotEmpty(timeTask.getId())) {
			message = "定時任務管理更新成功";
			TSTimeTaskEntity t = timeTaskService.get(TSTimeTaskEntity.class, timeTask.getId());
			try {
				if(!timeTask.getCronExpression().equals(t.getCronExpression())){
					timeTask.setIsEffect("0");
				}
				MyBeanUtils.copyBeanNotNull2Bean(timeTask, t);
				timeTaskService.saveOrUpdate(t);
				systemService.addLog(message, Globals.Log_Type_UPDATE, Globals.Log_Leavel_INFO);
			} catch (Exception e) {
				e.printStackTrace();
				message = "定時任務管理更新失敗";
			}
		} else {
			message = "定時任務管理新增成功";
			timeTaskService.save(timeTask);
			systemService.addLog(message, Globals.Log_Type_INSERT, Globals.Log_Leavel_INFO);
		}
		j.setMsg(message);
		return j;
		
	}

	/**
	 * 定時任務管理列表頁面跳轉
	 * 
	 * @return
	 */
	@RequestMapping(params = "addorupdate")
	public ModelAndView addorupdate(TSTimeTaskEntity timeTask, HttpServletRequest req) {
		if (StringUtil.isNotEmpty(timeTask.getId())) {
			timeTask = timeTaskService.getEntity(TSTimeTaskEntity.class, timeTask.getId());
			req.setAttribute("timeTaskPage", timeTask);
		}
		return new ModelAndView("system/timetask/timeTask");
	}
	
	/**
	 * 更新任務時間使之生效
	 */
	@RequestMapping(params = "updateTime")
	@ResponseBody
	public AjaxJson updateTime(TSTimeTaskEntity timeTask, HttpServletRequest request) {
		AjaxJson j = new AjaxJson();
		timeTask = timeTaskService.get(TSTimeTaskEntity.class, timeTask.getId());
		boolean isUpdate = dynamicTask.updateCronExpression(timeTask.getTaskId() , timeTask.getCronExpression());
		if(isUpdate){
			timeTask.setIsEffect("1");
			timeTask.setIsStart("1");
			timeTaskService.updateEntitie(timeTask);
		}
		j.setMsg(isUpdate?"定時任務管理更新成功":"定時任務管理更新失敗");
		return j;
	}
	/**
	 * 啟動或者停止任務
	 */
	@RequestMapping(params = "startOrStopTask")
	@ResponseBody
	public AjaxJson startOrStopTask(TSTimeTaskEntity timeTask, HttpServletRequest request) {
		AjaxJson j = new AjaxJson();
		boolean isStart = timeTask.getIsStart().equals("1");
		timeTask = timeTaskService.get(TSTimeTaskEntity.class, timeTask.getId());
		boolean isSuccess = false;
		try {
			isSuccess = dynamicTask.startOrStop(timeTask.getTaskId() ,isStart);
		} catch (Exception e) {
			j.setMsg(isSuccess?"定時任務管理更新成功":"定時任務管理更新失敗");
		}
		if(isSuccess){
			timeTask.setIsStart(isStart?"1":"0");
			timeTaskService.updateEntitie(timeTask);
			systemService.addLog((isStart?"開啟任務":"停止任務")+timeTask.getTaskId(),
					Globals.Log_Type_UPDATE, Globals.Log_Leavel_INFO);
		}
		j.setMsg(isSuccess?"定時任務管理更新成功":"定時任務管理更新失敗");
		return j;
	}
	
}

         動態調整定時任務:

/**
 * 動態任務,用以動態調整Spring的任務
 * @author JueYue
 * @date 2013-9-20
 * @version 1.0
 */
@Service(value="dynamicTask")
public class DynamicTask {
	
	private static Logger logger = Logger.getLogger(DynamicTask.class);

	@Resource
	private Scheduler schedulerFactory;
	
	/**
	 * 更新定時任務的觸發表示式
	 * 
	 * @param triggerName
	 *            觸發器名字
	 * @param start
	 *            觸發表示式
	 * @return 成功則返回true,否則返回false
	 */
	public boolean startOrStop(String triggerName,
			boolean start) {
		try {
			CronTrigger trigger = (CronTrigger) getTrigger(triggerName,
					Scheduler.DEFAULT_GROUP);
			if(start){
				schedulerFactory.resumeTrigger(trigger.getName(), trigger.getGroup());
				logger.info("trigger the start successfully!!");
			}else{
				schedulerFactory.pauseTrigger(trigger.getName(), trigger.getGroup());
				logger.info("trigger the pause successfully!!");
			}
			return true;
		}  catch (SchedulerException e) {
			logger.error("Fail to reschedule. " + e);
			return false;
		}
	}

	/**
	 * 更新定時任務的觸發表示式
	 * 
	 * @param triggerName
	 *            觸發器名字
	 * @param cronExpression
	 *            觸發表示式
	 * @return 成功則返回true,否則返回false
	 */
	public boolean updateCronExpression(String triggerName,
			String cronExpression) {
		try {
			CronTrigger trigger = (CronTrigger) getTrigger(triggerName,
					Scheduler.DEFAULT_GROUP);
			if (trigger == null) {
				return false;
			}
			if (StringUtils.equals(trigger.getCronExpression(), cronExpression)) {
				logger.info("cronExpression is same with the running Schedule , no need to update.");
				return true;
			}
			trigger.setCronExpression(cronExpression);
			schedulerFactory.rescheduleJob(trigger.getName(), trigger.getGroup(),
					trigger);
			updateSpringMvcTaskXML(trigger,cronExpression);
			logger.info("Update the cronExpression successfully!!");
			return true;
		} catch (ParseException e) {
			logger.error("The new cronExpression - " + cronExpression
					+ " not conform to the standard. " + e);
			return false;
		} catch (SchedulerException e) {
			logger.error("Fail to reschedule. " + e);
			return false;
		}
	}

	/**
	 * 獲取觸發器
	 * 
	 * @param triggerName
	 *            觸發器名字
	 * @param groupName
	 *            觸發器組名字
	 * @return 對應Trigger
	 */
	private Trigger getTrigger(String triggerName, String groupName) {
		Trigger trigger = null;
		if (StringUtils.isBlank(groupName)) {
			logger.warn("Schedule Job Group is empty!");
			return null;
		}
		if (StringUtils.isBlank(triggerName)) {
			logger.warn("Schedule trigger Name is empty!");
			return null;
		}
		try {
			trigger = schedulerFactory.getTrigger(triggerName, groupName);
		} catch (SchedulerException e) {
			logger.warn("Fail to get the trigger (triggerName: " + triggerName
					+ ", groupName : " + groupName + ")");
			return null;
		}
		if (trigger == null) {
			logger.warn("Can not found the trigger of triggerName: "
					+ triggerName + ", groupName : " + groupName);
		}
		return trigger;
	}
	/**
	 * 更新spring-mvc-timeTask.xml 配置檔案
	 * @param trigger
	 * @param cronExpression 
	 */
	@SuppressWarnings("unchecked")
	public synchronized static void updateSpringMvcTaskXML(CronTrigger trigger, String cronExpression) {
		Document document = null;
		File file = null;
		SAXReader saxReader = new SAXReader();
		try {
			URI url = DynamicTask.class.getClassLoader().getResource("spring-mvc-timeTask.xml").toURI();
			file = new File(url.getPath());
			document = saxReader.read(new FileInputStream(file));
		} catch (Exception e) {
			logger.error("讀取系統中用到的SQL 語句XML出錯");
			throw new RuntimeException("---------讀取spring-mvc-timeTask.xml檔案出錯:" + e.getMessage());
		}
		Element root = document.getRootElement();
		List<Element> beans = root.elements();
		for (Element bean : beans) {
			if(bean.attribute("id")!=null&&
					bean.attribute("id").getValue().equals(trigger.getName())){
				beans = bean.elements();
				for (Element temp : beans) {
					if(temp.attribute("name")!=null&&
							temp.attribute("name").getValue().equals("cronExpression")){
						temp.attribute("value").setValue(cronExpression);
						break;
					}
				}
				break;
			}
		}
		XMLWriter  fileWriter = null;
		try {
			OutputFormat xmlFormat = OutputFormat.createPrettyPrint();
			xmlFormat.setEncoding("utf-8");
			fileWriter = new XMLWriter(new FileOutputStream(file),xmlFormat);
			fileWriter.write(document);
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				fileWriter.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}

}

      在產品中,定時任務主要應用於統計資料,每日,每季度統計資料報表!是否建立會員使用者到期情況等。