spring之quartz的配置与使用
在项目中要使用定时启动某些应用的时候,我们可以用quartz定时器。那接下来讲一下在项目中的使用。
1. 首先要在maven中添加架包依赖
<!--定时任务框架-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.3</version>
</dependency>
2. spring配置文件
<!-- 项目启动开启定时对象和方法 -->
<bean id="cancelOrderJob"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<!-- 对象 -->
<property name="targetObject" ref="timersInfoService" />
<!-- 方法 -->
<property name="targetMethod" value="quartsJob" />
</bean>
<!-- 定时出发时间 -->
<bean id="cancelOrderTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<!-- 任务明细 -->
<property name="jobDetail" ref="cancelOrderJob" />
<!--cron表达式-->
<property name="cronExpression" value="0 0/1 * * * ?" /><!--每隔1小时0 0 */1 * * ?-->
</bean>
3. 表示层(Controller)代码TimersInfoController.java
@RequiresPermissions("timers:timersInfo:view")
@RequestMapping(value = "form")
public String form(TimersInfo timersInfo, Model model) {
model.addAttribute("timersInfo", timersInfo);
return "modules/timers/timersInfoForm";
}
@RequiresPermissions("timers:timersInfo:edit")
@RequestMapping(value = "save")
public String save(TimersInfo timersInfo, Model model, RedirectAttributes redirectAttributes) {
if (!beanValidator(model, timersInfo)) {
return form(timersInfo, model);
}
timersInfoService.save(timersInfo);
addMessage(redirectAttributes, "保存系统定时任务成功");
return "redirect:" + Global.getAdminPath() + "/timers/timersInfo/list?repage";
}
@RequiresPermissions("timers:timersInfo:edit")
@RequestMapping(value = "delete")
public String delete(TimersInfo timersInfo, RedirectAttributes redirectAttributes) {
timersInfoService.delete(timersInfo);
addMessage(redirectAttributes, "删除系统定时任务成功");
return "redirect:" + Global.getAdminPath() + "/timers/timersInfo/list?repage";
}
@RequiresPermissions("timers:timersInfo:edit")
@RequestMapping(value = "start")
public String start(TimersInfo timersInfo, RedirectAttributes redirectAttributes) {
timersInfoService.start(timersInfo);
addMessage(redirectAttributes, "开启系统定时任务成功");
return "redirect:" + Global.getAdminPath() + "/timers/timersInfo/list?repage";
}
@RequiresPermissions("timers:timersInfo:edit")
@RequestMapping(value = "shutdown")
public String shutdown(TimersInfo timersInfo, RedirectAttributes redirectAttributes){
timersInfoService.shutdown(timersInfo);
addMessage(redirectAttributes, "关闭系统定时任务成功");
return "redirect:" + Global.getAdminPath() + "/timers/timersInfo/list?repage";
}
@RequiresPermissions("timers:timersInfo:edit")
@RequestMapping(value = "checkIsExist")
@ResponseBody
public String checkIsExist(TimersInfo timersInfo) {
return timersInfoService.checkIsExist(timersInfo);
}
4. 逻辑业务层(Service)代码TimersInfoService.java
@Transactional(readOnly = false)
public void save(TimersInfo timersInfo) {
super.save(timersInfo);
}
@Transactional(readOnly = false)
public void delete(TimersInfo timersInfo) {
super.delete(timersInfo);
}
@Transactional(readOnly = false)
public void start(TimersInfo timersInfo) {
//开启定时
QuartzLoad.addJob(timersInfo);
//修改定时状态
timersInfo.preUpdate();
timersInfo.setTimerStatus("1");
timersInfoDao.updateTimerStatus(timersInfo);
}
@Transactional(readOnly = false)
public void shutdown(TimersInfo timersInfo) {
//移除定时
QuartzLoad.removeJob(timersInfo);
//修改定时状态
timersInfo.preUpdate();
timersInfo.setTimerStatus("0");
timersInfoDao.updateTimerStatus(timersInfo);
}
/**
* 项目启动开始的定时任务,负责开启库中开启的状态的定时任务
*/
public void quartsJob() {
//只执行一次
while (i++ == 0) {
List<TimersInfo> timersInfos = timersInfoDao.findTimerStatus();
for (TimersInfo timersInfo : timersInfos) {
//开启定时
QuartzLoad.addJob(timersInfo);
}
}
}
/**
* 校验所属区域是否存在
*/
public String checkIsExist(TimersInfo timersInfo) {
int retval = timersInfoDao.checkIsExist(timersInfo);
if (retval > 0) {
return "false";
}
return "true";
}
5. 定时器工具类QuartzLoad.java
public class QuartzLoad {
public static void addJob(TimersInfo timersInfo) {
try {
// String jobName, String jobGroupName, String triggerName, String triggerGroupName, String cron
// TODO: 2017/11/9/009 出发时间自己设定,为方便测试 设置为1分钟执行一次
// String cron = "0 0/1 * * * ?";
String cron = getCron(timersInfo.getExecTime());
// 任务名,任务组,任务执行类
JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) MyJob.class).withIdentity(timersInfo.getTimesName(), timersInfo.getRegionId()).build();
// 触发器
TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
// 触发器名,触发器组
triggerBuilder.withIdentity(timersInfo.getTimesName(), timersInfo.getId());
triggerBuilder.startNow();
// 触发器时间设定
triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
// 创建Trigger对象
CronTrigger trigger = (CronTrigger) triggerBuilder.build();
//创建scheduler对象
SchedulerFactory sfact = new StdSchedulerFactory();
Scheduler scheduler = sfact.getScheduler();
// scheduler.start();
// 调度容器设置JobDetail和Trigger
scheduler.scheduleJob(jobDetail, trigger);
// 启动
if (!scheduler.isShutdown()) {
scheduler.start();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//移除一个任务
public static void removeJob(TimersInfo timersInfoe) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(timersInfoe.getTimesName(), timersInfoe.getId());
//创建scheduler对象
SchedulerFactory sfact = new StdSchedulerFactory();
Scheduler scheduler = sfact.getScheduler();
//scheduler.start();
scheduler.pauseTrigger(triggerKey);// 停止触发器
scheduler.unscheduleJob(triggerKey);// 移除触发器
scheduler.deleteJob(JobKey.jobKey(timersInfoe.getTimesName(), timersInfoe.getRegionId()));// 删除任务
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//停止 关闭所有定时任务
public static void stop() throws Exception {
SchedulerFactory sfact = new StdSchedulerFactory();
Scheduler scheduler = sfact.getScheduler();
// scheduler.start();
scheduler.shutdown();
}
/**
* 获取时分秒的cron表达式
*
* @param time 时分秒(23:09:08)
* @return cron表达式
*/
public static String getCron(String time) {
String[] str = time.split(":");
StringBuffer sbf = new StringBuffer();
for (int i = (str.length - 1); i > -1; i--)
sbf.append(Integer.parseInt(str[i]) + " ");
sbf.append("* * ?");
return sbf.toString();
}
public static void main(String[] args) {
System.out.println(getCron("23:09:08"));
}
}
6. 使用定时器MyJob.java
该类要实现Job接口才能使用。
/**
* 统一处理定时业务
*/
public void execute(JobExecutionContext arg0) throws JobExecutionException {
TimersKeywordService service = (TimersKeywordService) SpringContextUtil.getBean("timersKeywordService",
TimersKeywordService.class);
logger.info("进入定时任务业务区===============================>>【begin】");
logger.info("能获取的参数:{编号[id]:" + ((CronTriggerImpl) ((JobExecutionContextImpl) arg0).getTrigger()).getGroup()
+ ",定时器名称[timesName]:" + ((CronTriggerImpl) ((JobExecutionContextImpl) arg0).getTrigger()).getName()
+ ",定时器名称[timesName]:" + arg0.getJobDetail().getKey().getName() + ",区域id[regionId]:"
+ arg0.getJobDetail().getKey().getGroup() + "}");
timersId = ((CronTriggerImpl) ((JobExecutionContextImpl) arg0).getTrigger()).getGroup();}
7. 界面展示timesInfoList.jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
<html>
<head>
<title>系统定时任务管理</title>
<meta name="decorator" content="default"/>
<script type="text/javascript">
$(document).ready(function() {
$("#btnDelete").click(function(){
var row = document.getElementsByName("box");
ids = [];
for(i in row){
if(row[i].checked){
ids.push(row[i].value);
}
}
if(ids.length == 0){
top.$.jBox.alert("请选择要删除的数据","系统提示");
}else{
top.$.jBox.confirm("确定删除选中的数据?","系统提示",function(v,h,f){
if(v=="ok"){
$("#searchForm").attr("action","${ctx}/timers/timersInfo/delete?id="+ids);
$("#searchForm").submit();
}
},{buttonsFocus:1});
}
top.$('.jbox-body .jbox-icon').css('top','55px');
});
$("#btnUpdate").click(function(){
var row = document.getElementsByName("box");
ids = [];
for(i in row){
if(row[i].checked){
ids.push(row[i].value);
}
}
if(ids.length == 0){
top.$.jBox.alert("请选择要修改的数据","系统提示");
}else if(ids.length == 1){
$("#searchForm").attr("action","${ctx}/timers/timersInfo/form?id="+ids);
$("#searchForm").submit();
}else {
top.$.jBox.alert("只能修改一条数据","系统提示");
}
top.$('.jbox-body .jbox-icon').css('top','55px');
});
$("#btnSave").click(function(){
$("#searchForm").attr("action","${ctx}/timers/timersInfo/form");
$("#searchForm").submit();
});
});
function page(n,s){
$("#pageNo").val(n);
$("#pageSize").val(s);
$("#searchForm").submit();
return false;
}
</script>
</head>
<body>
<form:form id="searchForm" modelAttribute="timersInfo" action="${ctx}/timers/timersInfo/" method="post" class="breadcrumb form-search">
<input id="pageNo" name="pageNo" type="hidden" value="${page.pageNo}"/>
<input id="pageSize" name="pageSize" type="hidden" value="${page.pageSize}"/>
<ul class="ul-form">
<li><label style="width: 85px;">定时器名称 :</label>
<form:input path="timesName" htmlEscape="false"
maxlength="50" class="input-medium" /></li>
<li><label style="width: 70px;">所属地区:</label> <sys:treeselect
id="company" name="regionId" value="${office.id}"
labelName="office.name" labelValue="${office.name}" title="归属区域"
url="/sys/office/treeData2?type=1" cssClass="input-small"
allowClear="true" notAllowSelectParent="true" /></li>
<li class="btns"><input id="btnSubmit" class="btn btn-primary" type="submit" value="查询" />
</li>
<shiro:hasPermission name="timers:timersInfo:edit">
<li class="btns"><input id="btnSave" class="btn btn-primary" type="button" value="添加"/></li>
<!-- <li class="btns"><input id="btnUpdate" class="btn btn-primary" type="button" value="修改"/></li>
<li class="btns"><input id="btnDelete" class="btn btn-primary" type="button" value="删除"/></li> -->
</shiro:hasPermission>
</ul>
</form:form>
<sys:message content="${message}"/>
<table id="contentTable" class="table table-striped table-bordered table-condensed">
<thead>
<tr>
<th>定时器名称</th>
<th>所属地区</th>
<th>定时任务状态</th>
<th>执行时间</th>
<shiro:hasPermission name="timers:timersInfo:edit"><th>操作</th></shiro:hasPermission>
</tr>
</thead>
<tbody>
<c:forEach items="${page.list}" var="timersInfo">
<tr>
<td>
<c:if test="${timersInfo.timerStatus ne '1'}">
<a href="${ctx}/timers/timersInfo/form?id=${timersInfo.id}">
${timersInfo.timesName}
</a>
</c:if>
<c:if test="${timersInfo.timerStatus eq '1'}">
${timersInfo.timesName}
</c:if>
</td>
<td>
${timersInfo.regionName}
</td>
<td>
<c:if test="${timersInfo.timerStatus eq '0'}"><c:out value="未开启"></c:out> </c:if>
<c:if test="${timersInfo.timerStatus eq '1'}"><c:out value="开启"></c:out> </c:if>
<c:if test="${timersInfo.timerStatus eq '2'}"><c:out value="已关闭"></c:out> </c:if>
</td>
<td>
${timersInfo.execTime}
</td>
<shiro:hasPermission name="timers:timersInfo:edit">
<td>
<c:if test="${timersInfo.timerStatus ne '1'}">
<a href="${ctx}/timers/timersInfo/form?id=${timersInfo.id}">修改</a>
<a href="${ctx}/timers/timersInfo/delete?id=${timersInfo.id}" οnclick="return confirmx('确认要删除该系统定时任务吗?', this.href)">删除</a>
<a href="${ctx}/timers/timersInfo/start?id=${timersInfo.id}" οnclick="return confirmx('确认要开启该系统定时任务吗?', this.href)">开启</a>
</c:if>
<c:if test="${timersInfo.timerStatus eq '1'}">
<a href="${ctx}/timers/timersInfo/shutdown?id=${timersInfo.id}" οnclick="return confirmx('确认要关闭该系统定时任务吗?', this.href)">关闭</a>
</c:if>
</td>
</shiro:hasPermission>
</tr>
</c:forEach>
</tbody>
</table>
<div class="pagination">${page}</div>
</body>
</html>
8. 界面展示timesInfoForm.jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ include file="/WEB-INF/views/include/taglib.jsp" %>
<html>
<head>
<title>系统定时任务管理</title>
<meta name="decorator" content="default"/>
<script type="text/javascript">
var fetch = true;
$(document).ready(function () {
$("#inputForm").validate({
rules: {
timesName: "required",
execTime: "required"
},
messages: {
timesName: "请输入定时器名称",
execTime: "请输入执行时间"
},
submitHandler: function (form) {
$.ajax({
type: 'POST',
cache: false,
async: false,
url: ctx + "/timers/timersInfo/checkIsExist",
data: {"id": $("#id").val(), "regionId": $("#regionId").val()},
success: function (data) {
if (data == "false" && fetch) {
fetch = false;
$("#tree").show();
return false;
}
}
});
if (!fetch) {
fetch = true;
return;
}
loading('正在提交,请稍等...');
form.submit();
},
errorContainer: "#messageBox",
errorPlacement: function (error, element) {
$("#messageBox").text("输入有误,请先更正。");
if (element.is(":checkbox") || element.is(":radio") || element.parent().is(".input-append")) {
error.appendTo(element.parent().parent());
} else {
error.insertAfter(element);
}
}
});
});
</script>
</head>
<body>
<ul class="nav nav-tabs">
<li><a href="${ctx}/timers/timersInfo/">系统定时任务列表</a></li>
<li class="active"><a href="${ctx}/timers/timersInfo/form?id=${timersInfo.id}">系统定时任务<shiro:hasPermission
name="timers:timersInfo:edit">${not empty timersInfo.id?'修改':'添加'}</shiro:hasPermission><shiro:lacksPermission
name="timers:timersInfo:edit">查看</shiro:lacksPermission></a></li>
</ul>
<br/>
<form:form id="inputForm" modelAttribute="timersInfo" action="${ctx}/timers/timersInfo/save" method="post"
class="form-horizontal">
<form:hidden path="id"/>
<sys:message content="${message}"/>
<div class="control-group">
<label class="control-label">所属地市:</label>
<div class="controls">
<sys:treeselect id="region" name="regionId" value="${timersInfo.regionId}" labelName="timersInfo.regionName"
labelValue="${timersInfo.regionName}"
title="区域" url="/sys/office/treeData2?type=1" cssClass="required"/>
<label for="regionId" class="error" style="display: none" id="tree">已存在该所属地市</label>
</div>
</div>
<div class="control-group">
<label class="control-label">定时器名称:</label>
<div class="controls">
<form:input path="timesName" htmlEscape="false" maxlength="64" class="input-xlarge "/>
</div>
</div>
<div class="control-group">
<label class="control-label">执行时间:</label>
<div class="controls">
<input id="execTime" name="execTime" type="text" readonly="readonly" maxlength="20"
class="input-medium Wdate"
value="${timersInfo.execTime}"
οnclick="WdatePicker({dateFmt:'HH:mm:ss',isShowClear:false});"/>
</div>
</div>
<div class="form-actions">
<shiro:hasPermission name="timers:timersInfo:edit"><input id="btnSubmit" class="btn btn-primary" type="submit"
value="保 存"/> </shiro:hasPermission>
<input id="btnCancel" class="btn" type="button" value="返 回" οnclick="history.go(-1)"/>
</div>
</form:form>
</body>
</html>
cron表达式:
格式: [秒] [分] [小时] [日] [月] [周] [年]
0 0 12 * * ? 每天12点触发
0 15 10 ? * * 每天10点15分触发
0 15 10 * * ? 每天10点15分触发
0 15 10 * * ? * 每天10点15分触发
0 15 10 * * ? 2005 2005年每天10点15分触发
0 * 14 * * ? 每天下午的 2点到2点59分每分触发
0 0/5 14 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发)
0 0/5 14,18 * * ? 每天下午的 18点到18点59分(整点开始,每隔5分触发)
0 0-5 14 * * ? 每天下午的 2点到2点05分每分触发
0 10,44 14 ? 3 WED 3月分每周三下午的 2点10分和2点44分触发
0 15 10 ? * MON-FRI 从周一到周五每天上午的10点15分触发
0 15 10 15 * ? 每月15号上午10点15分触发
0 15 10 L * ? 每月最后一天的10点15分触发
0 15 10 ? * 6L 每月最后一周的星期五的10点15分触发
0 15 10 ? * 6L 2002-2005 从2002年到2005年每月最后一周的星期五的10点15分触发
0 15 10 ? * 6#3 每月的第三周的星期五开始触发
0 0 12 1/5 * ? 每月的第一个中午开始每隔5天触发一次
0 11 11 11 11 ? 每年的11月11号 11点11分触发(光棍节)