RuoYi模块功能分析:第八章定时任务

2024-02-10 23:28

本文主要是介绍RuoYi模块功能分析:第八章定时任务,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

系列文章目录

文章目录

  • 系列文章目录
  • 引入依赖
  • 一、AbstractQuartzJob类
  • 二、QuartzJobExecution类
  • 三、QuartzDisallowConcurrentExecution类
  • 四、JobInvokeUtil类
  • 五、CronUtils类
  • 六、ScheduleUtils类


引入依赖

<!-- 定时任务 -->
<dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><exclusions><exclusion><groupId>com.mchange</groupId><artifactId>c3p0</artifactId></exclusion></exclusions>
</dependency>

一、AbstractQuartzJob类

改类位于package com.ruoyi.quartz.util;目录下。主要用于记录日志和定义执行任务是否支持并发执行的抽象方法

/*** 抽象quartz调用** @author ruoyi*/
public abstract class AbstractQuartzJob implements Job
{private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);/*** 线程本地变量*/private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException{// 创建任务对象SysJob sysJob = new SysJob();BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES));try{// 任务执行前before(context, sysJob);if (sysJob != null){// 执行任务doExecute(context, sysJob);}// 任务执行后after(context, sysJob, null);}catch (Exception e){log.error("任务执行异常  - :", e);// 记录日志after(context, sysJob, e);}}/*** 执行前,记录当前系统时间** @param context 工作执行上下文对象* @param sysJob 系统计划任务*/protected void before(JobExecutionContext context, SysJob sysJob){threadLocal.set(new Date());}/*** 执行后,记录执行后日志** @param context 工作执行上下文对象* @param sysJob 系统计划任务*/protected void after(JobExecutionContext context, SysJob sysJob, Exception e){// 获取开始时间Date startTime = threadLocal.get();// 清除本次线程数据threadLocal.remove();// 设置任务日志final SysJobLog sysJobLog = new SysJobLog();sysJobLog.setJobName(sysJob.getJobName());sysJobLog.setJobGroup(sysJob.getJobGroup());sysJobLog.setInvokeTarget(sysJob.getInvokeTarget());sysJobLog.setStartTime(startTime);sysJobLog.setStopTime(new Date());long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime();sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒");if (e != null){sysJobLog.setStatus(Constants.FAIL);// 获取并设置异常详细信息String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000);sysJobLog.setExceptionInfo(errorMsg);}else{sysJobLog.setStatus(Constants.SUCCESS);}// 写入数据库当中SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);}/*** 执行方法,由子类重载** @param context 工作执行上下文对象* @param sysJob 系统计划任务* @throws Exception 执行过程中的异常*/protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
}

二、QuartzJobExecution类

继承了AbstractQuartzJob,支持并发执行。位于package com.ruoyi.quartz.util;包下

/*** 定时任务处理(允许并发执行)* * @author ruoyi**/
public class QuartzJobExecution extends AbstractQuartzJob
{@Overrideprotected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception{// 执行目标方法JobInvokeUtil.invokeMethod(sysJob);}
}

三、QuartzDisallowConcurrentExecution类

继承了AbstractQuartzJob,不支持并发执行。位于package com.ruoyi.quartz.util;包下

/*** 定时任务处理(禁止并发执行)* * @author ruoyi**/
@DisallowConcurrentExecution
public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob
{@Overrideprotected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception{// 执行目标方法JobInvokeUtil.invokeMethod(sysJob);}
}

四、JobInvokeUtil类

该类主要用于获取执行目标的相关信息并且根据有无参数执行目标方法。位于package com.ruoyi.quartz.util;包下

/*** 任务执行工具** @author ruoyi*/
public class JobInvokeUtil
{/*** 执行方法** @param sysJob 系统任务*/public static void invokeMethod(SysJob sysJob) throws Exception{// 获取目标字符串String invokeTarget = sysJob.getInvokeTarget();// 获取bean名称String beanName = getBeanName(invokeTarget);// 获取方法名String methodName = getMethodName(invokeTarget);// 获取参数列表List<Object[]> methodParams = getMethodParams(invokeTarget);// 判断class名是否存在if (!isValidClassName(beanName)){// 不存在,直接获取bean对象Object bean = SpringUtils.getBean(beanName);invokeMethod(bean, methodName, methodParams);}else{// 存在,动态地根据一个类名加载并实例化一个对象Object bean = Class.forName(beanName).getDeclaredConstructor().newInstance();invokeMethod(bean, methodName, methodParams);}}/*** 调用任务方法** @param bean 目标对象* @param methodName 方法名称* @param methodParams 方法参数*/private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams)throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,InvocationTargetException{if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0){// 携带参数执行方法Method method = bean.getClass().getMethod(methodName, getMethodParamsType(methodParams));method.invoke(bean, getMethodParamsValue(methodParams));}else{// 无参执行方法Method method = bean.getClass().getMethod(methodName);method.invoke(bean);}}/*** 校验是否为为class包名* * @param invokeTarget 名称* @return true是 false否*/public static boolean isValidClassName(String invokeTarget){return StringUtils.countMatches(invokeTarget, ".") > 1;}/*** 获取bean名称* * @param invokeTarget 目标字符串* @return bean名称*/public static String getBeanName(String invokeTarget){// task.nameString beanName = StringUtils.substringBefore(invokeTarget, "(");// taskreturn StringUtils.substringBeforeLast(beanName, ".");}/*** 获取bean方法* * @param invokeTarget 目标字符串* @return method方法*/public static String getMethodName(String invokeTarget){// task.nameString methodName = StringUtils.substringBefore(invokeTarget, "(");// namereturn StringUtils.substringAfterLast(methodName, ".");}/*** 获取method方法参数相关列表* * @param invokeTarget 目标字符串* @return method方法相关参数列表*/public static List<Object[]> getMethodParams(String invokeTarget){// trueString methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");// 判断是否为空if (StringUtils.isEmpty(methodStr)){return null;}String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)");// 对参数进行过滤处理List<Object[]> classs = new LinkedList<>();for (int i = 0; i < methodParams.length; i++){String str = StringUtils.trimToEmpty(methodParams[i]);// String字符串类型,以'或"开头if (StringUtils.startsWithAny(str, "'", "\"")){classs.add(new Object[] { StringUtils.substring(str, 1, str.length() - 1), String.class });}// boolean布尔类型,等于true或者falseelse if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str)){classs.add(new Object[] { Boolean.valueOf(str), Boolean.class });}// long长整形,以L结尾else if (StringUtils.endsWith(str, "L")){classs.add(new Object[] { Long.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Long.class });}// double浮点类型,以D结尾else if (StringUtils.endsWith(str, "D")){classs.add(new Object[] { Double.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Double.class });}// 其他类型归类为整形else{classs.add(new Object[] { Integer.valueOf(str), Integer.class });}}return classs;}/*** 获取参数类型* * @param methodParams 参数相关列表* @return 参数类型列表*/public static Class<?>[] getMethodParamsType(List<Object[]> methodParams){Class<?>[] classs = new Class<?>[methodParams.size()];int index = 0;for (Object[] os : methodParams){classs[index] = (Class<?>) os[1];index++;}return classs;}/*** 获取参数值* * @param methodParams 参数相关列表* @return 参数值列表*/public static Object[] getMethodParamsValue(List<Object[]> methodParams){Object[] classs = new Object[methodParams.size()];int index = 0;for (Object[] os : methodParams){classs[index] = (Object) os[0];index++;}return classs;}
}

五、CronUtils类

该类主要用于校验cron表达式是否合法,计算下一次任务执行的时间。位于package com.ruoyi.quartz.util;包下

/*** cron表达式工具类* * @author ruoyi**/
public class CronUtils
{/*** 返回一个布尔值代表一个给定的Cron表达式的有效性** @param cronExpression Cron表达式* @return boolean 表达式是否有效*/public static boolean isValid(String cronExpression){return CronExpression.isValidExpression(cronExpression);}/*** 返回一个字符串值,表示该消息无效Cron表达式给出有效性** @param cronExpression Cron表达式* @return String 无效时返回表达式错误描述,如果有效返回null*/public static String getInvalidMessage(String cronExpression){try{new CronExpression(cronExpression);return null;}catch (ParseException pe){return pe.getMessage();}}/*** 返回下一个执行时间根据给定的Cron表达式** @param cronExpression Cron表达式* @return Date 下次Cron表达式执行时间*/public static Date getNextExecution(String cronExpression){try{CronExpression cron = new CronExpression(cronExpression);return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));}catch (ParseException e){throw new IllegalArgumentException(e.getMessage());}}
}

六、ScheduleUtils类

该类真真用于crud任务的工具类。位于package com.ruoyi.quartz.util;包下

/*** 定时任务工具类* * @author ruoyi**/
public class ScheduleUtils
{/*** 得到quartz任务类** @param sysJob 执行计划* @return 具体执行任务类*/private static Class<? extends Job> getQuartzJobClass(SysJob sysJob){// 是否允许并发(0允许,1禁止)boolean isConcurrent = "0".equals(sysJob.getConcurrent());return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;}/*** 构建任务触发对象*/public static TriggerKey getTriggerKey(Long jobId, String jobGroup){return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);}/*** 构建任务键对象*/public static JobKey getJobKey(Long jobId, String jobGroup){return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);}/*** 创建定时任务*/public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException{// 获取任务类(并行/串行)Class<? extends Job> jobClass = getQuartzJobClass(job);// 构建job信息Long jobId = job.getJobId();String jobGroup = job.getJobGroup();JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();// 表达式调度构建器CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);// 按新的cronExpression表达式构建一个新的triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup)).withSchedule(cronScheduleBuilder).build();// 放入参数,运行时的方法可以获取jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);// 判断是否存在if (scheduler.checkExists(getJobKey(jobId, jobGroup))){// 防止创建时存在数据问题 先移除,然后在执行创建操作scheduler.deleteJob(getJobKey(jobId, jobGroup));}// 判断任务是否过期if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))){// 执行调度任务scheduler.scheduleJob(jobDetail, trigger);}// 暂停任务if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())){scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));}}/*** 设置定时任务策略*/public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb)throws TaskException{switch (job.getMisfirePolicy()){case ScheduleConstants.MISFIRE_DEFAULT:return cb;case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:return cb.withMisfireHandlingInstructionIgnoreMisfires();case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:return cb.withMisfireHandlingInstructionFireAndProceed();case ScheduleConstants.MISFIRE_DO_NOTHING:return cb.withMisfireHandlingInstructionDoNothing();default:throw new TaskException("The task misfire policy '" + job.getMisfirePolicy()+ "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR);}}/*** 检查包名是否为白名单配置* * @param invokeTarget 目标字符串* @return 结果*/public static boolean whiteList(String invokeTarget){// task.name(true)// task.nameString packageName = StringUtils.substringBefore(invokeTarget, "(");// 计算.的个数int count = StringUtils.countMatches(packageName, ".");if (count > 1){// 判断是否为白名单return StringUtils.containsAnyIgnoreCase(invokeTarget, Constants.JOB_WHITELIST_STR);}// 获取任务bean对象Object obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]);// 获取任务bean对象路径String beanPackageName = obj.getClass().getPackage().getName();// 判断包是否在白名单并且不在错误名单return StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_WHITELIST_STR)&& !StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_ERROR_STR);}
}

这篇关于RuoYi模块功能分析:第八章定时任务的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/698263

相关文章

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数

MySQL 定时新增分区的实现示例

《MySQL定时新增分区的实现示例》本文主要介绍了通过存储过程和定时任务实现MySQL分区的自动创建,解决大数据量下手动维护的繁琐问题,具有一定的参考价值,感兴趣的可以了解一下... mysql创建好分区之后,有时候会需要自动创建分区。比如,一些表数据量非常大,有些数据是热点数据,按照日期分区MululbU

Python中re模块结合正则表达式的实际应用案例

《Python中re模块结合正则表达式的实际应用案例》Python中的re模块是用于处理正则表达式的强大工具,正则表达式是一种用来匹配字符串的模式,它可以在文本中搜索和匹配特定的字符串模式,这篇文章主... 目录前言re模块常用函数一、查看文本中是否包含 A 或 B 字符串二、替换多个关键词为统一格式三、提

Golang如何对cron进行二次封装实现指定时间执行定时任务

《Golang如何对cron进行二次封装实现指定时间执行定时任务》:本文主要介绍Golang如何对cron进行二次封装实现指定时间执行定时任务问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录背景cron库下载代码示例【1】结构体定义【2】定时任务开启【3】使用示例【4】控制台输出总结背景

在Golang中实现定时任务的几种高效方法

《在Golang中实现定时任务的几种高效方法》本文将详细介绍在Golang中实现定时任务的几种高效方法,包括time包中的Ticker和Timer、第三方库cron的使用,以及基于channel和go... 目录背景介绍目的和范围预期读者文档结构概述术语表核心概念与联系故事引入核心概念解释核心概念之间的关系

springboot如何通过http动态操作xxl-job任务

《springboot如何通过http动态操作xxl-job任务》:本文主要介绍springboot如何通过http动态操作xxl-job任务的问题,具有很好的参考价值,希望对大家有所帮助,如有错... 目录springboot通过http动态操作xxl-job任务一、maven依赖二、配置文件三、xxl-

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

一文详解MySQL如何设置自动备份任务

《一文详解MySQL如何设置自动备份任务》设置自动备份任务可以确保你的数据库定期备份,防止数据丢失,下面我们就来详细介绍一下如何使用Bash脚本和Cron任务在Linux系统上设置MySQL数据库的自... 目录1. 编写备份脚本1.1 创建并编辑备份脚本1.2 给予脚本执行权限2. 设置 Cron 任务2

Python logging模块使用示例详解

《Pythonlogging模块使用示例详解》Python的logging模块是一个灵活且强大的日志记录工具,广泛应用于应用程序的调试、运行监控和问题排查,下面给大家介绍Pythonlogging模... 目录一、为什么使用 logging 模块?二、核心组件三、日志级别四、基本使用步骤五、快速配置(bas

Django之定时任务django-crontab的实现

《Django之定时任务django-crontab的实现》Django可以使用第三方库如django-crontab来实现定时任务的调度,本文主要介绍了Django之定时任务django-cront... 目录crontab安装django-crontab注册应用定时时间格式定时时间示例设置定时任务@符号