前端時間開發接觸了一個開源框架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();
}
}
}
}
在產品中,定時任務主要應用於統計數據,每日,每季度統計數據報表!是否創建會員用戶到期情況等。
出現內存溢出時解決方案:http://blog.csdn.net/dslztx/article/details/47276953
學習參考資料:《Spring 整合 Quartz 實現動態定時任務》