本篇文章的目的是记录并实现在Spring Boot中,动态设定与执行定时任务。

我的开发项目是 Maven 项目,所以首先需要在 pom.xml 文件中加入相关的依赖。依赖代码如下所示:

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

下图是定时任务的列表及功能展示。

一般情况下,定时任务列表都是初始化生成,所以此处并无定时任务的新增功能。每个定时任务的操作栏中都有三种操作,分别是  频率设置、启用(禁用)、手动执行。

频率设置:设置定时任务的执行频率,设置成功后,可以重新设置定时任务的执行频率,这个功能是动态设定执行频率的保证。

启用(禁用):控制定时任务的执行状态,定时执行或者不执行。启用前,需先设置定时任务的执行频率。

手动执行:手动调用定时任务的执行方法,无需等到下次执行时间。手动执行前,定时任务的状态应该为启用状态。

在执行频率的设定上,我选择了直接使用Cron表达式,为了简单与方便,我在末端小图标上加了一个链接,点击图标后,便会跳转到在线Cron表达式页面。

<div class="row">
<label class="label col col-3 text-align-right">频率设置<font color="red">*</font>:</label>
<div class="col col-8">
<div class="input-group ">
<input type="text" class="form-control" id="cron" name="cron" th:value="${monitor.cron}" placeholder="点击末端小图标在线获取Cron表达式"/>
<span onclick="getCron()" class="input-group-addon"><i class="fa fa-calendar"></i></span>
</div>
</div>
</div>
//在线获取Cron表达式
function getCron() {
window.open("http://cron.qqe2.com/"); //打开一个新的网页,不会覆盖当前网页
}

在新打开的网页上,根据需求设定执行频率,将生成的Cron表达式复制到文本框中,点击 “确定” 按钮。下面的代码是点击 “确定” 按钮后,后台的处理逻辑。

  @RequestMapping(value = "setFrequencyCron", method = RequestMethod.POST)
@ResponseBody
@OperationLog(success = "设置频率成功", failure = "设置频率失败")
public Json setFrequencyCron(Monitor monitor) {
try {
Monitor m=monitorService.getMonitorById(monitor.getId());
m.setCron(monitor.getCron());
CronSequenceGenerator cronSequenceGenerator = new CronSequenceGenerator(monitor.getCron());
Date currentTime = new Date(); //当前系统时间
Date nextTimePoint = cronSequenceGenerator.next(currentTime); //下次执行时间
Date nextNextTimePoint = cronSequenceGenerator.next(nextTimePoint); //下下次执行时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String nextTime=sdf.format(nextTimePoint);
String nextDoubleTme=sdf.format(nextNextTimePoint);
m.setNextTime(nextTime);
m.setNextDoubleTime(nextDoubleTme);
m.setEnable("0"); //设置频率,让其状态变为“禁用”
monitorService.updateMonitor(m);
return Json.ok("频率设置成功");
} catch (Exception e) {
LOGGER.error("频率设置失败: " + e.getMessage(), e);
return Json.fail("频率设置失败");
}
}

根据Cron表达式,使用 Spring 自带的 CronSequenceGenerator 类可以获得下次执行时间和下下次执行时间。每次设定新的执行频率后,该定时任务的状态都会变为“禁用”,需要重新启用,定时任务才能生效。

下面的代码是点击 “启用”或“禁用” 按钮后,后台的处理逻辑。

    @RequestMapping(value = "setEnable", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
@OperationLog(success = "操作成功", failure = "操作失败")
public Json setEnable(Long id, String enable) {
Json json=new Json();
try {
String msg="";
Monitor monitor=monitorService.getMonitorById(id);
monitor.setEnable(enable);
monitorService.updateMonitor(monitor);
if (enable.equals("1")){
msg="启用成功";
}else if (enable.equals("0")){
msg="禁用成功";
} //启用或禁用时,清空redis中的监控信息
redisUtils.remove(KEY); json.setMsg(msg);
json.setSuccess(true);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
return Json.fail("操作失败");
}
return json;
}

在定时任务的执行过程中,为了减少与数据库的交互,所以使用了缓存工具类  RedisUtils ,会将最新的定时任务信息保存到 RedisUtils 中。所以在启用与禁用定时任务时,将会清除以前保存在 RedisUtils 中的定时任务信息,加载最新的定时任务信息。

在启动类的上方加上  @EnableScheduling 注解,该注解的作用是开启Spring Boot对定时任务的支持。

@EnableScheduling
public class DueUIApplication extends AbstractStdApplication {public static void main(String[] args) {
SpringApplication.run(DueUIApplication.class, args);
} }

新建一个定时任务执行类 MonitorTask  ,在该类中加一个用于扫描定时任务的方法,在该方法上方需要加上 @Scheduled 注解,同时需要带上参数,设定扫描频率。

代码如下所示:

@Component
public class MonitorTask extends AbstractStdAction {
private static final Logger LOGGER = Logger.getLogger(MonitorTask.class); @Autowired
private MonitorService monitorService; private String KEY="pbeechina:due:ui:monitor:action"; //redis的key /**
* 每隔一分钟秒扫描一次监控信息
*/
@Scheduled(cron = "0 0/1 * * * ? ")
@Transactional(propagation= Propagation.NOT_SUPPORTED) //不需要事物管理
public void scanMonitor(){
try {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("**********开始扫描监控的定时任务***********");
} List<Monitor> monitorList=null; //预警监控信息 boolean isKey=redisUtils.exists(KEY); //判断key值是否存在
if (isKey){ //key存在,代表监控信息是最新的
monitorList=(List<Monitor>)redisUtils.get(KEY);
}else {
monitorList=monitorService.getMonitorList(); //查询所有已被启用的监控
if (monitorList != null && monitorList.size() > 0){
redisUtils.set(KEY,monitorList, 1,TimeUnit.DAYS); //设置过期时间一天
}
} if (monitorList != null && monitorList.size() > 0){
for (Monitor monitor:monitorList){
if(StringUtils.isNotEmpty(monitor.getCron())){
CronExpression expression = new CronExpression(monitor.getCron());
if(expression.isSatisfiedBy(new Date(System.currentTimeMillis()))){
LOGGER.info("开始执行定时任务...");
monitorService.autoExecute(monitor);
}
}
}
} if (LOGGER.isDebugEnabled()) {
LOGGER.debug("*******扫描结束***********");
}
} catch (Exception e) {
LOGGER.error("监控定时任务扫描失败", e);
}
}
}

至此,Spring Boot动态执行定时任务的功能就实现了。

如何在Spring Boot 中动态设定与执行定时任务的更多相关文章

  1. 如何在Spring boot中修改默认端口

    文章目录 介绍 使用Property文件 在程序中指定 使用命令行参数 值生效的顺序 如何在Spring boot中修改默认端口 介绍 Spring boot为应用程序提供了很多属性的默认值.但是有时 ...

  2. 如何在Spring Boot中使用Cookies

    一. 导读 本文大纲 读取HTTP Cookie 设置HTTP Cookie 读取所有Cookie[] 为Cookie设置过期时间 Https与Cookie HttpOnly Cookie 删除Coo ...

  3. 在Spring Boot中动态实现定时任务配置

    原文路径:https://zhuanlan.zhihu.com/p/79644891 在日常的项目开发中,往往会涉及到一些需要做到定时执行的代码,例如自动将超过24小时的未付款的单改为取消状态,自动将 ...

  4. 如何在Spring Boot应用启动之后立刻执行一段逻辑

    1. 前言 不知道你有没有接到这种需求,项目启动后立马执行一些逻辑.比如简单的缓存预热,或者上线后的广播之类等等.如果你使用 Spring Boot 框架的话就可以借助其提供的接口CommandLin ...

  5. 如何在spring boot中从控制器返回一个html页面?

    项目截图 解决方法 我之前用的@RestController注解,而@RestController这个控制器返回数据而不是视图,改成@Controller 就好了(以下是修改后的) @Controll ...

  6. 如何在 Spring Boot 中禁用 Actuator 端点安全性?

    默认情况下,所有敏感的 HTTP 端点都是安全的,只有具有 ACTUATOR 角色的用户才能访问它们.安全性是使用标准的 HttpServletRequest.isUserInRole 方法实施的. ...

  7. (转)Spring Boot 2 (十):Spring Boot 中的响应式编程和 WebFlux 入门

    http://www.ityouknow.com/springboot/2019/02/12/spring-boot-webflux.html Spring 5.0 中发布了重量级组件 Webflux ...

  8. Spring Boot 2 (十):Spring Boot 中的响应式编程和 WebFlux 入门

    Spring 5.0 中发布了重量级组件 Webflux,拉起了响应式编程的规模使用序幕. WebFlux 使用的场景是异步非阻塞的,使用 Webflux 作为系统解决方案,在大多数场景下可以提高系统 ...

  9. Spring Boot中使用AOP统一处理Web请求日志

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是Spring框架中的一个重要内容,它通 ...

随机推荐

  1. 基础课(一)Cisco Packet Tracer Student模拟器简单的运用

    一.相同设备之间用交叉线,不同设备之间用直通线 如上图中的简单的两个PC机相连,交叉线相连的两个PC机是能ping同,直通线相连的两台PC机不通,ipconfig可以查看本机设备的网络配置 time= ...

  2. [ZJOI2010]排列计数 题解

    Description 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic ...

  3. MySQL server has gone away 解决办法

      Mysql 5.1 遇到的信息包过大问题 用客户端导入数据的时候,遇到 错误代码: 1153 - Got a packet bigger than 'max_allowed_packet' byt ...

  4. FVWM_SMALLEST_CONFIG

    ... # ----------------------------------------------------------------- # HANDBOOK: # http://yaoqian ...

  5. 卷积神经网络学习笔记(CNN)

    看了很多关于卷积神经网络的论文和资料 可是我发现一个问题,就是pooling会出现一个问题 我能找到的代码里面都是用的均值子采样,如果改成最大或最小,或P范数都会在学习训练的时候遇到不知道怎么处理的问 ...

  6. Java 并发理论简述

    一:为什么需要多线程? 线程是Java语言中不可或缺的重要部分,它们能使复杂的异步代码变得简单,简化复杂系统的开发:能充分发挥多处理器系统的强大计算能力.多线程和多进程的区别与选择可以参考我的另一篇博 ...

  7. [已解决]windows安装docker的问题

    windows下载安装docker出现的问题 进入powershell后输入docker --version报错: could not read CA certificate "C:\\Us ...

  8. 用python+tushare获取股票前复权后复权行情数据

    接口名称 :pro_bar 接口说明 :复权行情通过通用行情接口实现,利用Tushare Pro提供的复权因子进行计算,目前暂时只在SDK中提供支持,http方式无法调取. Python SDK版本要 ...

  9. 长度为x的本质不同的串的出现次数 SPOJ - NSUBSTR 后缀自动机简单应用

    题意: 长度为x的本质不同的串的出现次数 题解: 先处理出每一个节点所对应的子串出现的次数 然后取max就好了 #include <set> #include <map> #i ...

  10. 使用sql对比Mysql中数据库2个表结构

    比较两个数据表的结构 SELECT column_name, max( CASE WHEN table_name = 'table1' AND table_schema = 'db1' THEN 'Y ...