本文主要是介绍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模块功能分析:第八章定时任务的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!