Spring Boot集成持久化Quartz定时任务管理和界面展示

来源:互联网 发布:java获取post请求参数 编辑:程序博客网 时间:2024/06/15 09:30

前言

本文是对之前的一篇文章Spring+SpringMVC+mybatis+Quartz整合代码部分做的一个修改和补充, 其中最大的变化就是后台框架变成了Spring Boot。

本工程所用到的技术或工具有:

  • Spring Boot
  • Mybatis
  • Quartz
  • PageHelper
  • VueJS
  • ElementUI
  • MySql数据库

正文

配置

本例中仍然是使用mysql数据库作为Quartz任务持久化的存储载体。对于如何在Mysql数据库中建立表,在上一篇Spring+SpringMVC+mybatis+Quartz整合中已经有了详细的介绍。这里我们来看Spring Boot工程的建立方法。在Spring网站上有关于建立Spring Boot工程的脚手架,我们按如下图的方式设置,然后点击Generate Project即可。 


这里写图片描述

接着我们在IDE中导入这个maven工程,然后可以看到src/main/resource下面有一个名字为application.properties的文件,里面的内容是空的。我们删除这个文件,然后在这个目录下新建一个名为application.yml的文件。这是配置Spring Boot工程的另一种方式,也是Spring Boot官方推荐的一种配置方式。我们在新建的这个yml文件中,加入如下代码

spring:  datasource:    url: jdbc:mysql://190.0.1.88:3306/hello_test?useUnicode=true    username: root    password: root    driver-class-name: com.mysql.jdbc.Drivermybatis:   mapper-locations:  - classpath:com/example/demo/mapper/*.xml  type-aliases-package: com.example.demo.entity
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

上面的代码是对数据库和mybatis的一些配置。

接着我们在当前目录下再新建一个名为quartz.properties的文件。这是对Quartz的配置文件。加入如下代码:

# 固定前缀org.quartz# 主要分为scheduler、threadPool、jobStore、plugin等部分##org.quartz.scheduler.instanceName = DefaultQuartzSchedulerorg.quartz.scheduler.rmi.export = falseorg.quartz.scheduler.rmi.proxy = falseorg.quartz.scheduler.wrapJobExecutionInUserTransaction = false# 实例化ThreadPool时,使用的线程类为SimpleThreadPoolorg.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool# threadCount和threadPriority将以setter的形式注入ThreadPool实例# 并发个数org.quartz.threadPool.threadCount = 5# 优先级org.quartz.threadPool.threadPriority = 5org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = trueorg.quartz.jobStore.misfireThreshold = 5000# 默认存储在内存中#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore#持久化org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTXorg.quartz.jobStore.tablePrefix = QRTZ_org.quartz.jobStore.dataSource = qzDSorg.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driverorg.quartz.dataSource.qzDS.URL = jdbc:mysql://190.0.1.88:3306/hello_test?useUnicode=true&characterEncoding=UTF-8org.quartz.dataSource.qzDS.user = rootorg.quartz.dataSource.qzDS.password = rootorg.quartz.dataSource.qzDS.maxConnections = 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

可以看出和上一篇文章的配置文件完成相同。接着我们在com.example.demo下新建一个名为SchedulerConfig.java的文件。在这个文件里,对刚才我们新建的quartz.properties文件进行读取。

package com.example.demo;import java.io.IOException;import java.util.Properties;import javax.sql.DataSource;import org.quartz.ee.servlet.QuartzInitializerListener;import org.springframework.beans.factory.config.PropertiesFactoryBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.ClassPathResource;import org.springframework.scheduling.quartz.SchedulerFactoryBean;@Configurationpublic class SchedulerConfig {    @Bean    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) throws IOException {        SchedulerFactoryBean factory = new SchedulerFactoryBean();        factory.setQuartzProperties(quartzProperties());        return factory;    }    @Bean    public Properties quartzProperties() throws IOException {        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();        propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));        return propertiesFactoryBean.getObject();    }    @Bean    public QuartzInitializerListener executorListener() {       return new QuartzInitializerListener();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

注意最下方的QuartzInitializerListener。在SpringMVC中,我们在配置Quartz的时候,要在web.xml中加入如下配置:

    <listener>        <listener-class>            org.quartz.ee.servlet.QuartzInitializerListener        </listener-class>    </listener>
  • 1
  • 2
  • 3
  • 4
  • 5

这个监听器可以监听到工程的启动,在工程停止再启动时可以让已有的定时任务继续进行。由于我们目前的工程是Spring Boot,没有web.xml的配置方式,所以我们在上文的SchedulerConfig类中直接注入了这个Bean。

实现

先来看Job类。首先设置一个BaseJob接口,用来继承Job类:

package com.example.demo.job;import org.quartz.Job;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;public interface BaseJob extends Job{    public void execute(JobExecutionContext context) throws JobExecutionException;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

然后两个Job类用来实现BaseJob类: 
HelloJob

package com.example.demo.job;import java.util.Date;  import org.slf4j.Logger;  import org.slf4j.LoggerFactory;   import org.quartz.JobExecutionContext;  import org.quartz.JobExecutionException;  public class HelloJob implements BaseJob {      private static Logger _log = LoggerFactory.getLogger(HelloJob.class);      public HelloJob() {      }      public void execute(JobExecutionContext context)          throws JobExecutionException {          _log.error("Hello Job执行时间: " + new Date());      }  }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

NewJob

package com.example.demo.job;import java.util.Date;  import org.slf4j.Logger;  import org.slf4j.LoggerFactory;  import org.quartz.JobExecutionContext;  import org.quartz.JobExecutionException;  public class NewJob implements BaseJob {      private static Logger _log = LoggerFactory.getLogger(NewJob.class);      public NewJob() {      }      public void execute(JobExecutionContext context)          throws JobExecutionException {          _log.error("New Job执行时间: " + new Date());      }  }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

至于这样做的目的,我们可以先看一下Controller

package com.example.demo.controller;import java.util.HashMap;import java.util.Map;import org.quartz.CronScheduleBuilder;import org.quartz.CronTrigger;import org.quartz.JobBuilder;import org.quartz.JobDetail;import org.quartz.JobKey;import org.quartz.Scheduler;import org.quartz.SchedulerException;import org.quartz.SchedulerFactory;import org.quartz.TriggerBuilder;import org.quartz.TriggerKey;import org.quartz.impl.StdSchedulerFactory;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import com.example.demo.entity.JobAndTrigger;import com.example.demo.job.BaseJob;import com.example.demo.service.IJobAndTriggerService;import com.github.pagehelper.PageInfo;@RestController@RequestMapping(value="/job")public class JobController {    @Autowired    private IJobAndTriggerService iJobAndTriggerService;    private static Logger log = LoggerFactory.getLogger(JobController.class);      @PostMapping(value="/addjob")    public void addjob(@RequestParam(value="jobClassName")String jobClassName,             @RequestParam(value="jobGroupName")String jobGroupName,             @RequestParam(value="cronExpression")String cronExpression) throws Exception    {                   addJob(jobClassName, jobGroupName, cronExpression);    }    public static void addJob(String jobClassName, String jobGroupName, String cronExpression)throws Exception{        // 通过SchedulerFactory获取一个调度器实例          SchedulerFactory sf = new StdSchedulerFactory();              Scheduler sched = sf.getScheduler();          // 启动调度器          sched.start();         //构建job信息        JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobClassName, jobGroupName).build();        //表达式调度构建器(即任务执行的时间)        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);        //按新的cronExpression表达式构建一个新的trigger        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobClassName, jobGroupName)            .withSchedule(scheduleBuilder).build();        try {            sched.scheduleJob(jobDetail, trigger);        } catch (SchedulerException e) {            System.out.println("创建定时任务失败"+e);            throw new Exception("创建定时任务失败");        }    }    @PostMapping(value="/pausejob")    public void pausejob(@RequestParam(value="jobClassName")String jobClassName, @RequestParam(value="jobGroupName")String jobGroupName) throws Exception    {                   jobPause(jobClassName, jobGroupName);    }    public static void jobPause(String jobClassName, String jobGroupName) throws Exception    {        // 通过SchedulerFactory获取一个调度器实例          SchedulerFactory sf = new StdSchedulerFactory();                       Scheduler sched = sf.getScheduler();            sched.pauseJob(JobKey.jobKey(jobClassName, jobGroupName));    }    @PostMapping(value="/resumejob")    public void resumejob(@RequestParam(value="jobClassName")String jobClassName, @RequestParam(value="jobGroupName")String jobGroupName) throws Exception    {                   jobresume(jobClassName, jobGroupName);    }    public static void jobresume(String jobClassName, String jobGroupName) throws Exception    {         // 通过SchedulerFactory获取一个调度器实例          SchedulerFactory sf = new StdSchedulerFactory();                       Scheduler sched = sf.getScheduler();         sched.resumeJob(JobKey.jobKey(jobClassName, jobGroupName));    }    @PostMapping(value="/reschedulejob")    public void rescheduleJob(@RequestParam(value="jobClassName")String jobClassName,             @RequestParam(value="jobGroupName")String jobGroupName,            @RequestParam(value="cronExpression")String cronExpression) throws Exception    {                   jobreschedule(jobClassName, jobGroupName, cronExpression);    }    public static void jobreschedule(String jobClassName, String jobGroupName, String cronExpression) throws Exception    {                       try {            SchedulerFactory schedulerFactory = new StdSchedulerFactory();            Scheduler scheduler = schedulerFactory.getScheduler();            TriggerKey triggerKey = TriggerKey.triggerKey(jobClassName, jobGroupName);            // 表达式调度构建器            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);            // 按新的cronExpression表达式重新构建trigger            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();            // 按新的trigger重新设置job执行            scheduler.rescheduleJob(triggerKey, trigger);        } catch (SchedulerException e) {            System.out.println("更新定时任务失败"+e);            throw new Exception("更新定时任务失败");        }    }    @PostMapping(value="/deletejob")    public void deletejob(@RequestParam(value="jobClassName")String jobClassName, @RequestParam(value="jobGroupName")String jobGroupName) throws Exception    {                   jobdelete(jobClassName, jobGroupName);    }    public static void jobdelete(String jobClassName, String jobGroupName) throws Exception    {               // 通过SchedulerFactory获取一个调度器实例          SchedulerFactory sf = new StdSchedulerFactory();                       Scheduler sched = sf.getScheduler();         sched.pauseTrigger(TriggerKey.triggerKey(jobClassName, jobGroupName));        sched.unscheduleJob(TriggerKey.triggerKey(jobClassName, jobGroupName));        sched.deleteJob(JobKey.jobKey(jobClassName, jobGroupName));                 }    @GetMapping(value="/queryjob")    public Map<String, Object> queryjob(@RequestParam(value="pageNum")Integer pageNum, @RequestParam(value="pageSize")Integer pageSize)     {                   PageInfo<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;    }    public static BaseJob getClass(String classname) throws Exception     {        Class<?> class1 = Class.forName(classname);        return (BaseJob)class1.newInstance();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176

注意最下面的这个方法,根据类名称,通过反射得到该类,然后创建一个BaseJob的实例。由于NewJob和HelloJob都实现了BaseJob,所以这里不需要我们手动去判断。这里涉及到了一些java多态调用的机制,篇幅原因不多做解释。

其他的部分,例如service层,dao层以及mapper,与上一篇文章几乎完全相同。我们可以看一下现在的工程结构: 


这里写图片描述 

我们可以看到static下的JobManager.html,这个是前端的一个简单的管理页面。Spring Boot的web工程中,静态页面可以放在static目录下。这里贴一下代码,与之前几乎没区别:

<!DOCTYPE html><html><head><meta charset="UTF-8">    <title>QuartzDemo</title>    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-default/index.css">    <script src="https://unpkg.com/vue/dist/vue.js"></script>    <script src="http://cdn.bootcss.com/vue-resource/1.3.4/vue-resource.js"></script>    <script src="https://unpkg.com/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="handleadd" 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="cron_EXPRESSION"              label="表达式"              sortable>            </el-table-column>            <el-table-column              prop="time_ZONE_ID"              label="时区"              sortable>            </el-table-column>            <el-table-column label="操作" width="300">              <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>                <el-button                  size="small"                  type="success"                  @click="handleUpdate(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>         <el-dialog title="添加任务" v-model="dialogFormVisible">          <el-form :model="form">            <el-form-item label="任务名称" label-width="120px" style="width:35%">              <el-input v-model="form.jobName" auto-complete="off"></el-input>            </el-form-item>                 <el-form-item label="任务分组" label-width="120px" style="width:35%">              <el-input v-model="form.jobGroup" auto-complete="off"></el-input>            </el-form-item>            <el-form-item label="表达式" label-width="120px" style="width:35%">              <el-input v-model="form.cronExpression" auto-complete="off"></el-input>            </el-form-item>          </el-form>          <div slot="footer" class="dialog-footer">            <el-button @click="dialogFormVisible = false">取 消</el-button>            <el-button type="primary" @click="add">确 定</el-button>          </div>        </el-dialog>        <el-dialog title="修改任务" v-model="updateFormVisible">          <el-form :model="updateform">            <el-form-item label="表达式" label-width="120px" style="width:35%">              <el-input v-model="updateform.cronExpression" auto-complete="off"></el-input>            </el-form-item>          </el-form>          <div slot="footer" class="dialog-footer">            <el-button @click="updateFormVisible = false">取 消</el-button>            <el-button type="primary" @click="update">确 定</el-button>          </div>        </el-dialog>    </div>    <footer align="center">        <p>&copy; Quartz 任务管理</p>    </footer>    <script>    var vue = new Vue({                     el:"#test",            data: {                       //表格当前页数据                tableData: [],                //请求的URL                url:'job/queryjob',                //默认每页数据量                pagesize: 10,                               //当前页码                currentPage: 1,                //查询的页码                start: 1,                //默认数据总数                totalCount: 1000,                //添加对话框默认可见性                dialogFormVisible: false,                //修改对话框默认可见性                updateFormVisible: false,                //提交的表单                form: {                    jobName: '',                    jobGroup: '',                    cronExpression: '',                  },                updateform: {                    jobName: '',                    jobGroup: '',                    cronExpression: '',                },            },            methods: {                //从服务器读取数据                loadData: function(pageNum, pageSize){                                      this.$http.get('job/queryjob?' + 'pageNum=' +  pageNum + '&pageSize=' + pageSize).then(function(res){                        console.log(res)                        this.tableData = res.body.JobAndTrigger.list;                        this.totalCount = res.body.number;                    },function(){                        console.log('failed');                    });                                 },                                              //单行删除                handleDelete: function(index, row) {                    this.$http.post('job/deletejob',{"jobClassName":row.job_NAME,"jobGroupName":row.job_GROUP},{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,"jobGroupName":row.job_GROUP},{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,"jobGroupName":row.job_GROUP},{emulateJSON: true}).then(function(res){                        this.loadData( this.currentPage, this.pagesize);                    },function(){                        console.log('failed');                    });                },                //搜索                search: function(){                    this.loadData(this.currentPage, this.pagesize);                },                //弹出对话框                handleadd: function(){                                          this.dialogFormVisible = true;                                },                //添加                add: function(){                    this.$http.post('job/addjob',{"jobClassName":this.form.jobName,"jobGroupName":this.form.jobGroup,"cronExpression":this.form.cronExpression},{emulateJSON: true}).then(function(res){                        this.loadData(this.currentPage, this.pagesize);                        this.dialogFormVisible = false;                    },function(){                        console.log('failed');                    });                },                //更新                handleUpdate: function(index, row){                    console.log(row)                    this.updateFormVisible = true;                    this.updateform.jobName = row.job_CLASS_NAME;                    this.updateform.jobGroup = row.job_GROUP;                },                //更新任务                update: function(){                    this.$http.post                    ('job/reschedulejob',                            {"jobClassName":this.updateform.jobName,                             "jobGroupName":this.updateform.jobGroup,                             "cronExpression":this.updateform.cronExpression                             },{emulateJSON: true}                    ).then(function(res){                        this.loadData(this.currentPage, this.pagesize);                        this.updateFormVisible = false;                    },function(){                        console.log('failed');                    });                },                //每页显示数据量变更                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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305

唯一的区别是这里所用到的所有vue插件和ElementUI插件都是通过CDN方式引入,方便读者下载之后直接运行。需要注意的是,在添加新的任务的时候,填写任务名称时一定要把这个Job类的完整路径输入进来。例如 


这里写图片描述

否则会报找不到该类的错误。对于Cron表达式,可以去在线Cron表达式生成器根据自己的需求自动生成。程序的截图如下 


这里写图片描述

这两个任务执行的log:

2017-06-27 17:23:56.194 ERROR 9972 --- [eduler_Worker-3] com.example.demo.job.HelloJob: Hello Job执行时间: Tue Jun 27 17:23:56 CST 20172017-06-27 17:23:57.042 ERROR 9972 --- [eduler_Worker-4] com.example.demo.job.NewJob: New Job执行时间: Tue Jun 27 17:23:57 CST 20172017-06-27 17:23:58.051 ERROR 9972 --- [eduler_Worker-5] com.example.demo.job.HelloJob: Hello Job执行时间: Tue Jun 27 17:23:58 CST 20172017-06-27 17:24:00.070 ERROR 9972 --- [eduler_Worker-1] com.example.demo.job.NewJob: New Job执行时间: Tue Jun 27 17:24:00 CST 20172017-06-27 17:24:00.144 ERROR 9972 --- [eduler_Worker-2] com.example.demo.job.HelloJob: Hello Job执行时间: Tue Jun 27 17:24:00 CST 20172017-06-27 17:24:02.099 ERROR 9972 --- [eduler_Worker-3] com.example.demo.job.HelloJob: Hello Job执行时间: Tue Jun 27 17:24:02 CST 20172017-06-27 17:24:03.329 ERROR 9972 --- [eduler_Worker-4] com.example.demo.job.NewJob: New Job执行时间: Tue Jun 27 17:24:03 CST 2017
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

工程地址

这个工程已上传至我的github。如果你按上一篇文章的方法已经在mysql数据库中用脚本建立了数据表,那么下载这个工程之后只需要把application.yml和quartz.properties中对数据库连接的配置修改成你自己的数据库配置即可。

运行方法

Spring Boot工程已经集成了服务器。右键点击DemoApplication.java -> Run As -> Java Application即可运行工程。默认端口为8080,启动后在浏览器地址栏输入http://localhost:8080/JobManager.html就可以看到效果。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 总裁太爱我怎么办小说 小班走丢了怎么办ppt 走丢了怎么办 教案 ppt 小鸽子一大一小怎么办 烤八寸蛋糕胚表面上色严重怎么办 小班安全教案下雨打雷怎么办 幼儿园小班社会教案下雨打雷怎么办 两个月宝宝吃手怎么办 胳膊上长了个猴子怎么办 刚刚出壳的小鸡怎么办 南宁电动车牌被偷了怎么办 南宁电车车牌被偷了怎么办 电车车牌被偷了怎么办 上海电动车车牌被偷了怎么办 太子摩托车离合回的慢怎么办 指甲小月牙太少怎么办 牛仔裤用84泡了怎么办 蓝色的衣服晒红怎么办 厨房用的剪刀开合很紧怎么办 理发的剪刀钝了怎么办 小孩眼睛肿了怎么办才能消肿 柿子和螃蟹后要怎么办 柿子和螃蟹吃了怎么办 吃了没熟的虾怎么办 邻居小孩怕我家小狗怎么办 心里有一道坎过不去了怎么办 刚买的小狗怕人怎么办 一年级孩子字写不好怎么办 小狗三天没吃了怎么办 捡到一只流浪猫怎么办 仓鼠四肢红肿圈状怎么办 泰迪的鼻子干燥怎么办 小狗眼睛有白色浓稠物怎么办 流浪狗生了小狗怎么办 学生字写得很差怎么办 猫身上粘老鼠胶怎么办 抄东西抄的手疼怎么办 皮质物品被油性笔划了怎么办 在小区猫丢了怎么办 母猫把小猫丢了怎么办 小狗不吃东西没精神怎么办