Quartz与SpringMVC的整合

简介

Quartz是一个完全由java编写的开源作业调度框架,为在Java应用程序中进行作业调度提供了简单却强大的机制。Quartz允许开发人员根据时间间隔来调度作业。它实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。这篇文章介绍了Quartz与SSM框架的整合,包括了持久化的方法和对于任务的一些简单操作。本文包括一个简单的由vuejs和ElementUI开发的前端任务管理页面,对于vuejs和ElementUI的用法,在我的另一篇文章Vue2.0+ElementUI+PageHelper实现的表格分页中进行了详细的介绍,并且有完整的代码可供参考,这里不再赘述。

正文

Quartz的引入

在pom.xml中加入如下代码:

<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.</version>
</dependency> <dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.</version>
</dependency>

在web.xml中,加入如下代码:

 <context-param>
<param-name>quartz:config-file</param-name>
<param-value>scan-quartz.properties</param-value>
</context-param>
<context-param>
<param-name>quartz:shutdown-on-unload</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>quartz:wait-on-shutdown</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>quartz:start-scheduler-on-load</param-name>
<param-value>true</param-value>
</context-param>
<listener>
<listener-class>
org.quartz.ee.servlet.QuartzInitializerListener
</listener-class>
</listener>

scan-quartz.properties是quartz的配置文件,我们需要在resouces目录下加入新建一个名字为scan-quartz.properties的文件,然后在里面加入

# 固定前缀org.quartz
# 主要分为scheduler、threadPool、jobStore、plugin等部分
#
#
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false # 实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool # threadCount和threadPriority将以setter的形式注入ThreadPool实例
# 并发个数
org.quartz.threadPool.threadCount =
# 优先级
org.quartz.threadPool.threadPriority =
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true org.quartz.jobStore.misfireThreshold = # 默认存储在内存中
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

上面的最后一句话的作用是把任务内容存储在内存中。我们的程序是要做持久化任务,所以把上面的最后一句话注释掉,然后在下面加上

#持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.jobStore.dataSource = qzDS org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver org.quartz.dataSource.qzDS.URL = jdbc:mysql://190.0.1.88:3306/hello_test?useUnicode=true&characterEncoding=UTF-8 org.quartz.dataSource.qzDS.user = root org.quartz.dataSource.qzDS.password = root org.quartz.dataSource.qzDS.maxConnections =

上面代码主要是做了一些数据库的连接配置,如果大家用过mybatis,这些应该都能看懂。不过,事先要在数据库里创建一些表。具体的做法是打开数据库客户端,连接到某个数据库,然后新建一个查询。如果你用到的是mysql数据库,那么执行以下代码:

#
# Quartz seems to work best with the driver mm.mysql-2.0.-bin.jar
#
# PLEASE consider using mysql with innodb tables to avoid locking issues
#
# In your Quartz properties file, you'll need to set
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS; CREATE TABLE QRTZ_JOB_DETAILS
(
SCHED_NAME VARCHAR() NOT NULL,
JOB_NAME VARCHAR() NOT NULL,
JOB_GROUP VARCHAR() NOT NULL,
DESCRIPTION VARCHAR() NULL,
JOB_CLASS_NAME VARCHAR() NOT NULL,
IS_DURABLE VARCHAR() NOT NULL,
IS_NONCONCURRENT VARCHAR() NOT NULL,
IS_UPDATE_DATA VARCHAR() NOT NULL,
REQUESTS_RECOVERY VARCHAR() NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
); CREATE TABLE QRTZ_TRIGGERS
(
SCHED_NAME VARCHAR() NOT NULL,
TRIGGER_NAME VARCHAR() NOT NULL,
TRIGGER_GROUP VARCHAR() NOT NULL,
JOB_NAME VARCHAR() NOT NULL,
JOB_GROUP VARCHAR() NOT NULL,
DESCRIPTION VARCHAR() NULL,
NEXT_FIRE_TIME BIGINT() NULL,
PREV_FIRE_TIME BIGINT() NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR() NOT NULL,
TRIGGER_TYPE VARCHAR() NOT NULL,
START_TIME BIGINT() NOT NULL,
END_TIME BIGINT() NULL,
CALENDAR_NAME VARCHAR() NULL,
MISFIRE_INSTR SMALLINT() NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
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 VARCHAR() NOT NULL,
TRIGGER_NAME VARCHAR() NOT NULL,
TRIGGER_GROUP VARCHAR() NOT NULL,
REPEAT_COUNT BIGINT() NOT NULL,
REPEAT_INTERVAL BIGINT() NOT NULL,
TIMES_TRIGGERED BIGINT() NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
); CREATE TABLE QRTZ_CRON_TRIGGERS
(
SCHED_NAME VARCHAR() NOT NULL,
TRIGGER_NAME VARCHAR() NOT NULL,
TRIGGER_GROUP VARCHAR() NOT NULL,
CRON_EXPRESSION VARCHAR() NOT NULL,
TIME_ZONE_ID VARCHAR(),
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
); CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(
SCHED_NAME VARCHAR() NOT NULL,
TRIGGER_NAME VARCHAR() NOT NULL,
TRIGGER_GROUP VARCHAR() NOT NULL,
STR_PROP_1 VARCHAR() NULL,
STR_PROP_2 VARCHAR() NULL,
STR_PROP_3 VARCHAR() NULL,
INT_PROP_1 INT NULL,
INT_PROP_2 INT NULL,
LONG_PROP_1 BIGINT NULL,
LONG_PROP_2 BIGINT NULL,
DEC_PROP_1 NUMERIC(,) NULL,
DEC_PROP_2 NUMERIC(,) NULL,
BOOL_PROP_1 VARCHAR() NULL,
BOOL_PROP_2 VARCHAR() NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
); CREATE TABLE QRTZ_BLOB_TRIGGERS
(
SCHED_NAME VARCHAR() NOT NULL,
TRIGGER_NAME VARCHAR() NOT NULL,
TRIGGER_GROUP VARCHAR() NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
); CREATE TABLE QRTZ_CALENDARS
(
SCHED_NAME VARCHAR() NOT NULL,
CALENDAR_NAME VARCHAR() NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
); CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
(
SCHED_NAME VARCHAR() NOT NULL,
TRIGGER_GROUP VARCHAR() NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
); CREATE TABLE QRTZ_FIRED_TRIGGERS
(
SCHED_NAME VARCHAR() NOT NULL,
ENTRY_ID VARCHAR() NOT NULL,
TRIGGER_NAME VARCHAR() NOT NULL,
TRIGGER_GROUP VARCHAR() NOT NULL,
INSTANCE_NAME VARCHAR() NOT NULL,
FIRED_TIME BIGINT() NOT NULL,
SCHED_TIME BIGINT() NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR() NOT NULL,
JOB_NAME VARCHAR() NULL,
JOB_GROUP VARCHAR() NULL,
IS_NONCONCURRENT VARCHAR() NULL,
REQUESTS_RECOVERY VARCHAR() NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID)
); CREATE TABLE QRTZ_SCHEDULER_STATE
(
SCHED_NAME VARCHAR() NOT NULL,
INSTANCE_NAME VARCHAR() NOT NULL,
LAST_CHECKIN_TIME BIGINT() NOT NULL,
CHECKIN_INTERVAL BIGINT() NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
); CREATE TABLE QRTZ_LOCKS
(
SCHED_NAME VARCHAR() NOT NULL,
LOCK_NAME VARCHAR() NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME)
); commit;

如果是其他的数据库,可以从quartz官网下载完整的文档,然后在docs目录下的dbTables文件夹里找到对应的创建表的方法。

后台代码

先创建两个任务类HelloJob和NewJob

import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException; public class HelloJob implements Job { private static Logger _log = LoggerFactory.getLogger(HelloJob.class); public HelloJob() { } public void execute(JobExecutionContext context)
throws JobExecutionException {
_log.error("Hello Job执行时间: " + new Date()); }
}
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException; public class NewJob implements Job { private static Logger _log = LoggerFactory.getLogger(HelloJob.class); public NewJob() { } public void execute(JobExecutionContext context)
throws JobExecutionException {
_log.error("New Job执行时间: " + new Date()); }
}

这两个任务类的执行都是简单的打印执行时间。 
接下来是Controller,首先是添加任务

@ResponseBody
@RequestMapping(value="/addjob", method = RequestMethod.POST)
public void addjob(@RequestParam(value="jobClassName")String jobClassName) throws Exception
{
setJob(jobClassName);
} public static void setJob(String jobClassName) throws Exception
{
// 通过SchedulerFactory获取一个调度器实例
SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); // 启动调度器
sched.start(); switch (jobClassName)
{
case "HelloJob":
JobDetail job = newJob(HelloJob.class).withIdentity("HelloJob", "group1").build();
Trigger trigger = newTrigger().withIdentity("HelloJob", "group1").startNow().withSchedule(simpleSchedule()
.withIntervalInSeconds()
.repeatForever()).build();
sched.scheduleJob(job, trigger);
break; case "NewJob":
JobDetail job2 = newJob(NewJob.class).withIdentity("NewJob", "group1").build();
Trigger trigger2 = newTrigger().withIdentity("NewJob", "group1").startNow().withSchedule(simpleSchedule()
.withIntervalInSeconds()
.repeatForever()).build();
sched.scheduleJob(job2, trigger2);
break; default:
break;
}
}

从前端接收一个名称参数,然后根据这个名称创建对应的任务实例。每个任务都有个触发器,这里两个任务都设置为每10秒钟运行一次,永久循环。每个任务实例都需要一个名称和组,对于两个任务实例来说,名称和组不能全部相同,因为它们在寻找任务实例中起到key的作用。

接下来是暂停任务。

 @ResponseBody
@RequestMapping(value="/pausejob", method = RequestMethod.POST)
public void pausejob(@RequestParam(value="jobClassName")String jobClassName) throws Exception
{
jobPause(jobClassName);
} public static void jobPause(String jobClassName) throws Exception
{
// 通过SchedulerFactory获取一个调度器实例
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
switch (jobClassName) {
case "HelloJob":
sched.pauseJob(JobKey.jobKey("HelloJob", "group1"));
break; case "NewJob":
sched.pauseJob(JobKey.jobKey("NewJob", "group1"));
break; default:
break;
}
}

可以看到上文通过jobkey找到了这个任务的实例,然后进行暂停操作。 
接下来恢复。

@ResponseBody
@RequestMapping(value="/resumejob", method = RequestMethod.POST)
public void resumejob(@RequestParam(value="jobClassName")String jobClassName) throws Exception
{
jobresume(jobClassName);
} public static void jobresume(String jobClassName) throws Exception
{
// 通过SchedulerFactory获取一个调度器实例
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
if(sched != null){
switch (jobClassName) {
case "HelloJob":
sched.resumeJob(JobKey.jobKey("HelloJob", "group1"));
break; case "NewJob":
sched.resumeJob(JobKey.jobKey("NewJob", "group1"));
break; default:
break;
}
}
}

然后是删除操作。

 @ResponseBody
@RequestMapping(value="/deletejob", method = RequestMethod.POST)
public void deletejob(@RequestParam(value="jobClassName")String jobClassName) throws Exception
{
jobdelete(jobClassName);
} public static void jobdelete(String jobClassName) throws Exception
{
// 通过SchedulerFactory获取一个调度器实例
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
switch (jobClassName) {
case "HelloJob":
sched.pauseTrigger(TriggerKey.triggerKey("HelloJob", "group1"));
sched.unscheduleJob(TriggerKey.triggerKey("HelloJob", "group1"));
sched.deleteJob(JobKey.jobKey("HelloJob", "group1"));
break; case "NewJob":
sched.pauseTrigger(TriggerKey.triggerKey("NewJob", "group1"));
sched.unscheduleJob(TriggerKey.triggerKey("NewJob", "group1"));
sched.deleteJob(JobKey.jobKey("NewJob", "group1"));
break; default:
break;
} }

删除操作前应该暂停该任务的触发器,并且停止该任务的执行。

最后是查询,这里用到了pageHelper插件,具体的也是写在了Vue2.0+ElementUI+PageHelper实现的表格分页中。

@ResponseBody
@RequestMapping(value="/queryjob")
public Map<String, Object> queryjob(@RequestParam(value="pageNum")Integer pageNum, @RequestParam(value="pageSize")Integer pageSize)
{
Page<JobAndTrigger> jobAndTrigger = iJobAndTriggerService.getJobAndTriggerDetails(pageNum, pageSize);
Map<String, Object> map = new HashMap<String, Object>();
map.put("JobAndTrigger", jobAndTrigger);
map.put("number", jobAndTrigger.getTotal());
return map;
}

这里主要的作用是将数据库里存储的关于任务和触发器的部分信息查询出来在前端显示。附上mybatis中的sql代码

<select id="getJobAndTriggerDetails" resultType="com.hdnav.core.entity.JobAndTrigger">
SELECT
qrtz_job_details.JOB_NAME,
qrtz_job_details.JOB_GROUP,
qrtz_job_details.JOB_CLASS_NAME,
qrtz_triggers.TRIGGER_NAME,
qrtz_triggers.TRIGGER_GROUP,
qrtz_simple_triggers.REPEAT_INTERVAL,
qrtz_simple_triggers.TIMES_TRIGGERED
FROM
qrtz_job_details
JOIN qrtz_triggers
JOIN qrtz_simple_triggers ON qrtz_job_details.JOB_NAME = qrtz_triggers.JOB_NAME
AND qrtz_triggers.TRIGGER_NAME = qrtz_simple_triggers.TRIGGER_NAME
AND qrtz_triggers.TRIGGER_GROUP = qrtz_simple_triggers.TRIGGER_GROUP
</select>

最后显示的效果如图所示

右键图片在新标签页打开图片可以看得更清晰。 
下面是控制台输出

[com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr  :: CST
[com.hdnav.core.HelloJob] - Hello Job执行时间: Thu Apr :: CST
[com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
[com.hdnav.core.HelloJob] - Hello Job执行时间: Thu Apr :: CST
[com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
[com.hdnav.core.HelloJob] - Hello Job执行时间: Thu Apr :: CST
[com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
[com.hdnav.core.HelloJob] - Hello Job执行时间: Thu Apr :: CST
[com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
[com.hdnav.core.HelloJob] - Hello Job执行时间: Thu Apr :: CST

可以看到两个任务都是每隔10秒执行一次。 
若点击暂停,则该任务暂停执行。点击恢复则会恢复执行。 
若执行删除操作,则数据表内不再有该任务的数据,只有重新添加任务才会显示,不过所有的数据都会重置。

[com.hdnav.core.HelloJob] - Hello Job执行时间: Thu Apr  :: CST
[com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
[com.hdnav.core.HelloJob] - Hello Job执行时间: Thu Apr :: CST
[com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
[com.hdnav.core.dao.JobAndTriggerMapper.getJobAndTriggerDetails] - ==> Preparing: SELECT qrtz_job_details.JOB_NAME, qrtz_job_details.JOB_GROUP, qrtz_job_details.JOB_CLASS_NAME, qrtz_triggers.TRIGGER_NAME, qrtz_triggers.TRIGGER_GROUP, qrtz_simple_triggers.REPEAT_INTERVAL, qrtz_simple_triggers.TIMES_TRIGGERED FROM qrtz_job_details JOIN qrtz_triggers JOIN qrtz_simple_triggers ON qrtz_job_details.JOB_NAME = qrtz_triggers.JOB_NAME AND qrtz_triggers.TRIGGER_NAME = qrtz_simple_triggers.TRIGGER_NAME AND qrtz_triggers.TRIGGER_GROUP = qrtz_simple_triggers.TRIGGER_GROUP LIMIT
[com.hdnav.core.dao.JobAndTriggerMapper.getJobAndTriggerDetails] - ==> Parameters:
[com.hdnav.core.dao.JobAndTriggerMapper.getJobAndTriggerDetails] - <== Total:
[com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
[com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
[com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST

可以看到当执行删除HelloJob操作之后,控制台不再有指令。此时数据表中该条数据也被删除。

前端代码

前端代码比较简单,这里不再做过多的解释。

<html>
<head>
<meta charset="UTF-8">
<title>QuartzDemo</title>
<link rel="stylesheet" href="/core/element-ui/lib/theme-default/index.css">
<script src="./js/jquery-3.1.1.min.js"></script>
<script src="./js/json2.js"></script>
<script src="./js/vue.min.js"></script>
<script src="./js/vue-resource.js"></script>
<script src="./element-ui/lib/index.js"></script> <style>
#top {
background:#20A0FF;
padding:5px;
overflow:hidden
}
</style> </head>
<body>
<div id="test"> <div id="top">
<el-button type="text" @click="search" style="color:white">查询</el-button>
<el-button type="text" @click="add" style="color:white">添加</el-button>
</span>
</div> <br/> <div style="margin-top:15px"> <el-table
ref="testTable"
:data="tableData"
style="width:100%"
border
>
<el-table-column
prop="job_NAME"
label="任务名称"
sortable
show-overflow-tooltip>
</el-table-column> <el-table-column
prop="job_GROUP"
label="任务所在组"
sortable>
</el-table-column> <el-table-column
prop="job_CLASS_NAME"
label="任务类名"
sortable>
</el-table-column> <el-table-column
prop="trigger_NAME"
label="触发器名称"
sortable>
</el-table-column> <el-table-column
prop="trigger_GROUP"
label="触发器所在组"
sortable>
</el-table-column> <el-table-column
prop="repeat_INTERVAL"
label="触发间隔(毫秒)"
sortable>
</el-table-column> <el-table-column
prop="times_TRIGGERED"
label="已触发次数"
sortable>
</el-table-column> <el-table-column label="操作">
<template scope="scope">
<el-button
size="small"
type="warning"
@click="handlePause(scope.$index, scope.row)">暂停</el-button> <el-button
size="small"
type="info"
@click="handleResume(scope.$index, scope.row)">恢复</el-button> <el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table> <div align="center">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pagesize"
layout="total, sizes, prev, pager, next, jumper"
:total="totalCount">
</el-pagination>
</div>
</div>
</div> <footer align="center">
<p>&copy; Quartz 任务管理</p>
</footer> <script>
var vue = new Vue({
el:"#test",
data: {
//表格当前页数据
tableData: [], //请求的URL
url:'job/queryjob', //默认每页数据量
pagesize: , //当前页码
currentPage: , //查询的页码
start: , //默认数据总数
totalCount: ,
}, methods: { //从服务器读取数据
loadData: function(pageNum, pageSize){
this.$http.get(this.url,{pageNum:pageNum, pageSize:pageSize}).then(function(res){
this.tableData = res.data.JobAndTrigger;
this.totalCount = res.data.number;
},function(){
console.log('failed');
});
}, //单行删除
handleDelete: function(index, row) {
this.$http.post('job/deletejob',{"jobClassName":row.job_NAME},{emulateJSON: true}).then(function(res){
this.loadData( this.currentPage, this.pagesize);
},function(){
console.log('failed');
});
}, handlePause: function(index, row){
this.$http.post('job/pausejob',{"jobClassName":row.job_NAME},{emulateJSON: true}).then(function(res){
this.loadData( this.currentPage, this.pagesize);
},function(){
console.log('failed');
});
}, handleResume: function(index, row){
this.$http.post('job/resumejob',{"jobClassName":row.job_NAME},{emulateJSON: true}).then(function(res){
this.loadData( this.currentPage, this.pagesize);
},function(){
console.log('failed');
});
}, //搜索
search: function(){
this.loadData(this.currentPage, this.pagesize);
}, //添加
add: function(){
this.$prompt('请输入名称', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
}).then(({ value }) => {
if(value==''||value==null)
return;
this.$http.post('job/addjob',{"jobClassName":value},{emulateJSON: true}).then(function(res){
this.loadData(this.currentPage, this.pagesize);
},function(){
console.log('failed');
});
}).catch(() => { }); }, //每页显示数据量变更
handleSizeChange: function(val) {
this.pagesize = val;
this.loadData(this.currentPage, this.pagesize);
}, //页码变更
handleCurrentChange: function(val) {
this.currentPage = val;
this.loadData(this.currentPage, this.pagesize);
}, }, }); //载入数据
vue.loadData(vue.currentPage, vue.pagesize);
</script> </body>
</html>

至此,一个简单的SSM框架与Quartz的整合工程便做好了。虽然是一个很简单的Demo,但是功能还算完整。对于人物的修改,后台的代码如下:

@ResponseBody
@RequestMapping(value="/reschedulejob", method = RequestMethod.POST)
public void rescheduleJob(@RequestParam(value="jobClassName")String jobClassName) throws Exception
{
jobreschedule(jobClassName);
} public static void jobreschedule(String jobClassName) throws Exception
{
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
TriggerKey triggerKey = TriggerKey.triggerKey(jobClassName, "group1");
Trigger newTrigger = scheduler.getTrigger(triggerKey);
scheduler.rescheduleJob(triggerKey, newTrigger);
scheduler.start();
}

当然这里的trigger可以重新自定义,达到修改任务的效果。不过和前端结合相对来说比较麻烦,所以没有写前端的代码。做这个Demo的主要目的是为了更直观的感受到Quartz的可持久化特性,并且与SpringMVC相结合,可以从一个简单的前端界面直接去操作和管理这些任务。

结束语

这个小小的Demo所展现出的功能只是quartz茫茫多的功能的冰山一角,不过quartz是一个比较简单易懂的开源框架,文档相对来说很全面,在企业级的web程序开发中也完全可以胜任。相对于这个小小的Demo,还有很多更强大的功能等待着我们去探索。

Spring+SpringMVC+mybatis+Quartz整合的更多相关文章

  1. Spring+SpringMVC+MyBatis+easyUI整合基础篇(六)maven整合SSM

    写在前面的话   承接前文<Spring+SpringMVC+MyBatis+easyUI整合基础篇(五)讲一下maven>,本篇所讲述的是如何使用maven与原ssm项目整合,使得一个普 ...

  2. Spring+SpringMVC+MyBatis+easyUI整合基础篇(八)mysql中文查询bug修复

    写在前面的话 在测试搜索时出现的问题,mysql通过中文查询条件搜索不出数据,但是英文和数字可以搜索到记录,中文无返回记录.本文就是写一下发现问题的过程及解决方法.此bug在第一个项目中点这里还存在, ...

  3. Spring+SpringMVC+MyBatis+easyUI整合基础篇(十一)SVN服务器进阶

    日常啰嗦 上一篇文章<Spring+SpringMVC+MyBatis+easyUI整合基础篇(十)SVN搭建>简单的讲了一下SVN服务器的搭建,并没有详细的介绍配置文件及一些复杂的功能, ...

  4. Spring+SpringMVC+MyBatis+easyUI整合基础篇(十二)阶段总结

    不知不觉,已经到了基础篇的收尾阶段了,看着前面的十几篇文章,真的有点不敢相信,自己竟然真的坚持了下来,虽然过程中也有过懒散和焦虑,不过结果还是自己所希望的,克服了很多的问题,将自己的作品展现出来,也发 ...

  5. Spring+SpringMVC+MyBatis+easyUI整合优化篇(二)Log4j讲解与整合

    日常啰嗦 上一篇文章主要讲述了一下syso和Log间的一些区别与比较,重点是在项目的日志功能上,因此,承接前文<Spring+SpringMVC+MyBatis+easyUI整合优化篇(一)Sy ...

  6. Spring+SpringMVC+MyBatis+easyUI整合优化篇(四)单元测试实例

    日常啰嗦 前一篇文章<Spring+SpringMVC+MyBatis+easyUI整合优化篇(三)代码测试>讲了不为和不能两个状态,针对不为,只能自己调整心态了,而对于不能,本文会结合一 ...

  7. Spring+SpringMVC+MyBatis+easyUI整合优化篇(五)结合MockMvc进行服务端的单元测试

    日常啰嗦 承接前一篇文章<Spring+SpringMVC+MyBatis+easyUI整合优化篇(四)单元测试实例>,已经讲解了dao层和service层的单元测试,还有控制器这层也不能 ...

  8. Spring+SpringMVC+MyBatis+easyUI整合优化篇(七)图片上传功能

    日常啰嗦 前一篇文章<Spring+SpringMVC+MyBatis+easyUI整合优化篇(六)easyUI与富文本编辑器UEditor整合>讲了富文本编辑器UEditor的整合与使用 ...

  9. Spring+SpringMVC+MyBatis+easyUI整合优化篇(十三)数据层优化-表规范、索引优化

    本文提要 最近写的几篇文章都是关于数据层优化方面的,这几天也在想还有哪些地方可以优化改进,结合日志和项目代码发现,关于数据层的优化,还是有几个方面可以继续修改的,代码方面,整合了druid数据源也开启 ...

随机推荐

  1. Win10交换Ctrl和大写键

    打开注册表 [HKEY_LOCAL_MacHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout] "Scancode Map" ...

  2. eclipse运行程序时报java.lang.OutOfMemoryError: Java heap space内存不足问题

    System.setProperty("webdriver.firefox.bin", "D:\\Mozilla Firefox\\firefox.exe"); ...

  3. myeclipse 保存失败

    Save FailedCompilation unit name must end with .java, or one of the registered Java-like extensions ...

  4. php解析url并得到url中的参数

    <?php $url = 'http://www.baidu.com/index.php?m=content&c=index&a=lists&catid=6&ar ...

  5. 机器学习入门-主成分分析(PCA)

    主成分分析: 用途:降维中最常用的一种方法 目标:提取有用的信息(基于方差的大小) 存在的问题:降维后的数据将失去原本的数据意义 向量的内积:A*B = |A|*|B|*cos(a) 如果|B| = ...

  6. 创建一个多进程(multiprocessing.Process)

    进程是资源的集合,每个进程至少包含一个线程 import multiprocessing #导入进程模块import time, threading #导入线程 def thread_run(): p ...

  7. AS3 os与version 区别 使用Capabilities类获取Flash Player的信息

    AS3中flash.system.Capabilities类提供诸多静态的只读属性来描述应用程序当前所运行在的系统和运行时信息,如Flash Player,Adobe AIR,Flash Lite.通 ...

  8. 一个简单例子弄懂什么是javascript函数劫持

    javascript函数劫持很简单,一般情况下,只要在目标函数触发之前,重写这个函数即可. 比如,劫持eval函数的代码如下: var _eval=eval; eval=function(x){ if ...

  9. python中时间差中seconds和total_seconds

    在python中经常会用到计算两个时间差,两个日期类型进行相减可以获取到时间差 经常会使用seconds来获取,其实seconds获取的是时间差的秒数,遗漏了天 seconds是获取时间部分的差值,而 ...

  10. iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 错误处理

    沪江CCtalk视频地址:https://www.cctalk.com/v/15114923887518 处理错误请求 爱能遮掩一切过错. 当我们在访问一个站点的时候,如果访问的地址不存在(404), ...