访问量 次
访客数 人
代码结构
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
-- ----------------------------
-- 定时任务调度表
-- ----------------------------
drop table if exists sys_job;
create table sys_job (
job_id bigint(20) not null auto_increment comment '任务ID',
job_name varchar(64) default '' comment '任务名称',
job_group varchar(64) default 'DEFAULT' comment '任务组名',
invoke_target varchar(500) not null comment '调用目标字符串',
cron_expression varchar(255) default '' comment 'cron执行表达式',
misfire_policy varchar(20) default '3' comment '计划执行错误策略(1立即执行 2执行一次 3放弃执行)',
concurrent char(1) default '1' comment '是否并发执行(0允许 1禁止)',
status char(1) default '0' comment '状态(0正常 1暂停)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default '' comment '备注信息',
primary key (job_id, job_name, job_group)
) engine=innodb auto_increment=100 comment = '定时任务调度表';
-- ----------------------------
-- 定时任务调度日志表
-- ----------------------------
drop table if exists sys_job_log;
create table sys_job_log (
job_log_id bigint(20) not null auto_increment comment '任务日志ID',
job_name varchar(64) not null comment '任务名称',
job_group varchar(64) not null comment '任务组名',
invoke_target varchar(500) not null comment '调用目标字符串',
job_message varchar(500) comment '日志信息',
status char(1) default '0' comment '执行状态(0正常 1失败)',
exception_info varchar(2000) default '' comment '异常信息',
create_time datetime comment '创建时间',
primary key (job_log_id)
) engine=innodb comment = '定时任务调度日志表';
/**
* @author: whitepure
* @date: 2023/7/20 13:35
* @description: JobIndexController
*/
@Controller
public class JobManagerController {
@RequestMapping({"/","/index"})
public String index(){
return "index.html";
}
}
@RestController
@RequestMapping("/main")
public class SysJobController {
@Autowired
private ISysJobService jobService;
/**
* 查询定时任务列表
*/
@GetMapping("/list")
public R<List<SysJob>> list(SysJob sysJob) {
return R.ok(jobService.selectJobList(sysJob));
}
/**
* 获取定时任务详细信息
*/
@GetMapping(value = "/getInfo")
public R<SysJob> getInfo(Long jobId) {
return R.ok(jobService.selectJobById(jobId));
}
/**
* 新增定时任务
*/
@PostMapping("/add")
public R<Boolean> add(@RequestBody SysJob job) throws SchedulerException, TaskException {
if (!CronUtils.isValid(job.getCronExpression())) {
return R.failed("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确");
} else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), ScheduleConstants.LOOKUP_RMI)) {
return R.failed("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用");
} else if (StrUtil.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{ScheduleConstants.LOOKUP_LDAP, ScheduleConstants.LOOKUP_LDAPS})) {
return R.failed("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用");
} else if (StrUtil.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{ScheduleConstants.HTTP, ScheduleConstants.HTTPS})) {
return R.failed("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用");
} else if (StrUtil.containsAnyIgnoreCase(job.getInvokeTarget(), ScheduleConstants.JOB_ERROR_STR)) {
return R.failed("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规");
} else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) {
return R.failed("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内");
}
return R.ok(jobService.insertJob(job) > 0);
}
/**
* 修改定时任务
*/
@PostMapping("/edit")
public R<Boolean> edit(@RequestBody SysJob job) throws SchedulerException, TaskException {
if (!CronUtils.isValid(job.getCronExpression())) {
return R.failed("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确");
} else if (StrUtil.containsIgnoreCase(job.getInvokeTarget(), ScheduleConstants.LOOKUP_RMI)) {
return R.failed("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用");
} else if (StrUtil.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{ScheduleConstants.LOOKUP_LDAP, ScheduleConstants.LOOKUP_LDAPS})) {
return R.failed("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用");
} else if (StrUtil.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{ScheduleConstants.HTTP, ScheduleConstants.HTTPS})) {
return R.failed("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用");
} else if (StrUtil.containsAnyIgnoreCase(job.getInvokeTarget(), ScheduleConstants.JOB_ERROR_STR)) {
return R.failed("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规");
} else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) {
return R.failed("修改任务'" + job.getJobName() + "'失败,目标字符串不在白名单内");
}
return R.ok(jobService.updateJob(job) > 0);
}
/**
* 定时任务状态修改
*/
@PostMapping("/changeStatus")
public R<Boolean> changeStatus(@RequestBody SysJob job) throws SchedulerException {
SysJob newJob = jobService.selectJobById(job.getJobId());
newJob.setStatus(job.getStatus());
return R.ok(jobService.changeStatus(newJob) > 0);
}
/**
* 定时任务立即执行一次
*/
@GetMapping("/run")
public R<Boolean> run(Long jobId) throws SchedulerException {
boolean result = jobService.run(jobId);
return result ? R.ok(true): R.failed("任务不存在或已过期!");
}
/**
* 删除定时任务
*/
@GetMapping("/remove")
public R<Boolean> remove(Long jobId) throws SchedulerException {
jobService.deleteJobByIds(new Long[]{jobId});
return R.ok(true);
}
}
@RestController
@RequestMapping("/jobLog")
public class SysJobLogController {
@Autowired
private ISysJobLogService jobLogService;
/**
* 查询定时任务调度日志列表
*/
@GetMapping("/list")
public R<List<SysJobLog>> list(SysJobLog sysJobLog) {
return R.ok(jobLogService.selectJobLogList(sysJobLog));
}
/**
* 根据调度编号获取详细信息
*/
@GetMapping(value = "/getInfo")
public R<SysJobLog> getInfo(Long logId) {
return R.ok(jobLogService.selectJobLogById(logId));
}
/**
* 删除定时任务调度日志
*/
@PostMapping("/{jobLogIds}")
public R<Boolean> remove(@PathVariable Long[] jobLogIds) {
return R.ok(jobLogService.deleteJobLogByIds(jobLogIds) > 0);
}
/**
* 清空定时任务调度日志
*/
@GetMapping("/clean")
public R<Boolean> clean() {
jobLogService.cleanJobLog();
return R.ok(true);
}
}
@Slf4j
@RestControllerAdvice("com.chenglian.scheduled")
public class JobExceptionHandler {
@ExceptionHandler(Exception.class)
public R<Boolean> globalHandler(Exception exception) {
log.error("定时任务程序发生异常.", exception);
return R.failed(exception.getMessage());
}
}
public class TaskException extends Exception {
private static final long serialVersionUID = 1L;
private final Code code;
public TaskException(String msg, Code code) {
this(msg, code, null);
}
public TaskException(String msg, Code code, Exception nestedEx) {
super(msg, nestedEx);
this.code = code;
}
public Code getCode() {
return code;
}
public enum Code {
TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE
}
}
public interface IErrorCode {
long getCode();
String getMsg();
}
@Getter
@ToString
public enum ApiErrorCode implements IErrorCode {
FAILED(-1L, "操作失败"),
SUCCESS(0L, "执行成功");
private final long code;
private final String msg;
ApiErrorCode(final long code, final String msg) {
this.code = code;
this.msg = msg;
}
public static ApiErrorCode fromCode(long code) {
ApiErrorCode[] ecs = values();
ApiErrorCode[] var3 = ecs;
int var4 = ecs.length;
for(int var5 = 0; var5 < var4; ++var5) {
ApiErrorCode ec = var3[var5];
if (ec.getCode() == code) {
return ec;
}
}
return SUCCESS;
}
}
@Data
@EqualsAndHashCode
public class R<T> implements Serializable {
private static final long serialVersionUID = 1L;
private long code;
private T data;
private String msg;
public R() {
}
public R(IErrorCode errorCode) {
errorCode = (IErrorCode) Optional.ofNullable(errorCode).orElse(ApiErrorCode.FAILED);
this.code = errorCode.getCode();
this.msg = errorCode.getMsg();
}
public static <T> R<T> ok(T data) {
ApiErrorCode aec = ApiErrorCode.SUCCESS;
if (data instanceof Boolean && Boolean.FALSE.equals(data)) {
aec = ApiErrorCode.FAILED;
}
return restResult(data, aec);
}
public static <T> R<T> failed(String msg) {
return restResult(null, ApiErrorCode.FAILED.getCode(), msg);
}
public static <T> R<T> failed(IErrorCode errorCode) {
return restResult(null, errorCode);
}
public static <T> R<T> restResult(T data, IErrorCode errorCode) {
return restResult(data, errorCode.getCode(), errorCode.getMsg());
}
private static <T> R<T> restResult(T data, long code, String msg) {
R<T> apiResult = new R();
apiResult.setCode(code);
apiResult.setData(data);
apiResult.setMsg(msg);
return apiResult;
}
public boolean ok() {
return ApiErrorCode.SUCCESS.getCode() == this.code;
}
}
public interface ISysJobLogService {
/**
* 获取quartz调度器日志的计划任务
*
* @param jobLog 调度日志信息
* @return 调度任务日志集合
*/
List<SysJobLog> selectJobLogList(SysJobLog jobLog);
/**
* 通过调度任务日志ID查询调度信息
*
* @param jobLogId 调度任务日志ID
* @return 调度任务日志对象信息
*/
SysJobLog selectJobLogById(Long jobLogId);
/**
* 新增任务日志
*
* @param jobLog 调度日志信息
*/
void addJobLog(SysJobLog jobLog);
/**
* 批量删除调度日志信息
*
* @param logIds 需要删除的日志ID
* @return 结果
*/
int deleteJobLogByIds(Long[] logIds);
/**
* 删除任务日志
*
* @param jobId 调度日志ID
* @return 结果
*/
int deleteJobLogById(Long jobId);
/**
* 清空任务日志
*/
void cleanJobLog();
}
public interface ISysJobService {
/**
* 获取quartz调度器的计划任务
*
* @param job 调度信息
* @return 调度任务集合
*/
List<SysJob> selectJobList(SysJob job);
/**
* 通过调度任务ID查询调度信息
*
* @param jobId 调度任务ID
* @return 调度任务对象信息
*/
SysJob selectJobById(Long jobId);
/**
* 暂停任务
*
* @param job 调度信息
* @return 结果
*/
int pauseJob(SysJob job) throws SchedulerException;
/**
* 恢复任务
*
* @param job 调度信息
* @return 结果
*/
int resumeJob(SysJob job) throws SchedulerException;
/**
* 删除任务后,所对应的trigger也将被删除
*
* @param job 调度信息
* @return 结果
*/
int deleteJob(SysJob job) throws SchedulerException;
/**
* 批量删除调度信息
*
* @param jobIds 需要删除的任务ID
* @return 结果
*/
void deleteJobByIds(Long[] jobIds) throws SchedulerException;
/**
* 任务调度状态修改
*
* @param job 调度信息
* @return 结果
*/
int changeStatus(SysJob job) throws SchedulerException;
/**
* 立即运行任务
*
* @param jobId 调度任务ID
* @return 结果
*/
boolean run(Long jobId) throws SchedulerException;
/**
* 新增任务
*
* @param job 调度信息
* @return 结果
*/
int insertJob(SysJob job) throws SchedulerException, TaskException;
/**
* 更新任务
*
* @param job 调度信息
* @return 结果
*/
int updateJob(SysJob job) throws SchedulerException, TaskException, TaskException;
/**
* 校验cron表达式是否有效
*
* @param cronExpression 表达式
* @return 结果
*/
boolean checkCronExpressionIsValid(String cronExpression);
}
public abstract class AbstractQuartzJob implements Job {
private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);
/**
* 线程本地变量
*/
private static final ThreadLocal<Date> threadLocal = new ThreadLocal<>();
@Override
public void execute(JobExecutionContext context) {
SysJob sysJob = new SysJob();
BeanUtil.copyProperties(context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES),sysJob);
try {
before(context, sysJob);
if (sysJob.getJobId() != 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(ScheduleConstants.FAIL);
String errorMsg = StringUtils.substring(getExceptionMessage(e), 0, 2000);
sysJobLog.setExceptionInfo(errorMsg);
} else {
sysJobLog.setStatus(ScheduleConstants.SUCCESS);
}
// 写入数据库当中
SpringUtil.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
}
public static String getExceptionMessage(Throwable e) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw, true));
return sw.toString();
}
/**
* 执行方法,由子类重载
*
* @param context 工作执行上下文对象
* @param sysJob 系统计划任务
* @throws Exception 执行过程中的异常
*/
protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
}
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());
}
}
}
public class JobInvokeUtil {
/**
* 执行方法
*
* @param sysJob 系统任务
*/
public static void invokeMethod(SysJob sysJob) throws Exception {
String invokeTarget = sysJob.getInvokeTarget();
String beanName = getBeanName(invokeTarget);
String methodName = getMethodName(invokeTarget);
List<Object[]> methodParams = getMethodParams(invokeTarget);
if (!isValidClassName(beanName)) {
Object bean = SpringUtil.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 (ObjectUtil.isNotEmpty(methodParams) && !methodParams.isEmpty()) {
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) {
String beanName = StringUtils.substringBefore(invokeTarget, "(");
return StringUtils.substringBeforeLast(beanName, ".");
}
/**
* 获取bean方法
*
* @param invokeTarget 目标字符串
* @return method方法
*/
public static String getMethodName(String invokeTarget) {
String methodName = StringUtils.substringBefore(invokeTarget, "(");
return StringUtils.substringAfterLast(methodName, ".");
}
/**
* 获取method方法参数相关列表
*
* @param invokeTarget 目标字符串
* @return method方法相关参数列表
*/
public static List<Object[]> getMethodParams(String invokeTarget) {
String 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或者false
else 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] = os[0];
index++;
}
return classs;
}
}
@DisallowConcurrentExecution
public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob {
@Override
protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
JobInvokeUtil.invokeMethod(sysJob);
}
}
public class QuartzJobExecution extends AbstractQuartzJob {
@Override
protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
JobInvokeUtil.invokeMethod(sysJob);
}
}
public class ScheduleConstants {
public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";
/**
* 执行目标key
*/
public static final String TASK_PROPERTIES = "TASK_PROPERTIES";
/**
* 默认
*/
public static final String MISFIRE_DEFAULT = "0";
/**
* 立即触发执行
*/
public static final String MISFIRE_IGNORE_MISFIRES = "1";
/**
* 触发一次执行
*/
public static final String MISFIRE_FIRE_AND_PROCEED = "2";
/**
* 不触发立即执行
*/
public static final String MISFIRE_DO_NOTHING = "3";
public enum Status {
/**
* 正常
*/
NORMAL("0"),
/**
* 暂停
*/
PAUSE("1");
private final String value;
Status(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
/**
* http请求
*/
public static final String HTTP = "http://";
/**
* https请求
*/
public static final String HTTPS = "https://";
/**
* 通用成功标识
*/
public static final String SUCCESS = "0";
/**
* 通用失败标识
*/
public static final String FAIL = "1";
/**
* RMI 远程方法调用
*/
public static final String LOOKUP_RMI = "rmi:";
/**
* LDAP 远程方法调用
*/
public static final String LOOKUP_LDAP = "ldap:";
/**
* LDAPS 远程方法调用
*/
public static final String LOOKUP_LDAPS = "ldaps:";
/**
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
*/
public static final String[] JOB_WHITELIST_STR = {"com.chenglian"};
/**
* 定时任务违规的字符
*/
public static final String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
"org.springframework", "org.apache", "com.chenglian.common.utils.file", "com.chenglian.common.config"};
}
public class ScheduleUtils {
/**
* 得到quartz任务类
*
* @param sysJob 执行计划
* @return 具体执行任务类
*/
private static Class<? extends Job> getQuartzJobClass(SysJob sysJob) {
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表达式构建一个新的trigger
CronTrigger 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 (Objects.nonNull(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", TaskException.Code.CONFIG_ERROR);
}
}
/**
* 检查包名是否为白名单配置
*
* @param invokeTarget 目标字符串
* @return 结果
*/
public static boolean whiteList(String invokeTarget) {
String packageName = StringUtils.substringBefore(invokeTarget, "(");
int count = StringUtils.countMatches(packageName, ".");
if (count > 1) {
return CharSequenceUtil.containsAnyIgnoreCase(invokeTarget, ScheduleConstants.JOB_WHITELIST_STR);
}
Object obj = SpringUtil.getBean(StringUtils.split(invokeTarget, ".")[0]);
String beanPackageName = obj.getClass().getPackage().getName();
return CharSequenceUtil.containsAnyIgnoreCase(beanPackageName, ScheduleConstants.JOB_WHITELIST_STR)
&& !CharSequenceUtil.containsAnyIgnoreCase(beanPackageName, ScheduleConstants.JOB_ERROR_STR);
}
}