springboot動態管理定時任務


1.表 

  job任務表

CREATE TABLE `sys_job` (
  `job_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任務ID',
  `job_name` varchar(64) NOT NULL DEFAULT '' COMMENT '任務名稱',
  `job_group` varchar(64) NOT NULL 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 DEFAULT NULL COMMENT '創建時間',
  `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
  `update_time` datetime DEFAULT NULL COMMENT '更新時間',
  `remark` varchar(500) DEFAULT '' COMMENT '備注信息',
  PRIMARY KEY (`job_id`,`job_name`,`job_group`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='定時任務調度表';

  任務日志表

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) DEFAULT NULL COMMENT '日志信息',
  `status` char(1) DEFAULT '0' COMMENT '執行狀態(0正常 1失敗)',
  `exception_info` varchar(2000) DEFAULT '' COMMENT '異常信息',
  `create_time` datetime DEFAULT NULL COMMENT '創建時間',
  PRIMARY KEY (`job_log_id`)
) ENGINE=InnoDB AUTO_INCREMENT=173 DEFAULT CHARSET=utf8 COMMENT='定時任務調度日志表';

2.實體類

  基礎實體類

package com.ruoyi.common.core.domain;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;

/**
 * Entity基類
 * 
 * @author ruoyi
 */
public class BaseEntity implements Serializable
{
    private static final long serialVersionUID = 1L;

    /** 搜索值 */
    private String searchValue;

    /** 創建者 */
    private String createBy;

    /** 創建時間 */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;

    /** 更新者 */
    private String updateBy;

    /** 更新時間 */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;

    /** 備注 */
    private String remark;

    /** 開始時間 */
    @JsonIgnore
    private String beginTime;

    /** 結束時間 */
    @JsonIgnore
    private String endTime;

    /** 請求參數 */
    private Map<String, Object> params;

    public String getSearchValue()
    {
        return searchValue;
    }

    public void setSearchValue(String searchValue)
    {
        this.searchValue = searchValue;
    }

    public String getCreateBy()
    {
        return createBy;
    }

    public void setCreateBy(String createBy)
    {
        this.createBy = createBy;
    }

    public Date getCreateTime()
    {
        return createTime;
    }

    public void setCreateTime(Date createTime)
    {
        this.createTime = createTime;
    }

    public String getUpdateBy()
    {
        return updateBy;
    }

    public void setUpdateBy(String updateBy)
    {
        this.updateBy = updateBy;
    }

    public Date getUpdateTime()
    {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime)
    {
        this.updateTime = updateTime;
    }

    public String getRemark()
    {
        return remark;
    }

    public void setRemark(String remark)
    {
        this.remark = remark;
    }

    public String getBeginTime()
    {
        return beginTime;
    }

    public void setBeginTime(String beginTime)
    {
        this.beginTime = beginTime;
    }

    public String getEndTime()
    {
        return endTime;
    }

    public void setEndTime(String endTime)
    {
        this.endTime = endTime;
    }

    public Map<String, Object> getParams()
    {
        if (params == null)
        {
            params = new HashMap<>();
        }
        return params;
    }

    public void setParams(Map<String, Object> params)
    {
        this.params = params;
    }
}

  JOB實體類

package com.ruoyi.quartz.domain;

import java.io.Serializable;
import java.util.Date;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.quartz.util.CronUtils;

/**
 * 定時任務調度表 sys_job
 * 
 * @author ruoyi
 */
public class SysJob extends BaseEntity implements Serializable
{
    private static final long serialVersionUID = 1L;

    /** 任務ID */
    @Excel(name = "任務序號", cellType = ColumnType.NUMERIC)
    private Long jobId;

    /** 任務名稱 */
    @Excel(name = "任務名稱")
    private String jobName;

    /** 任務組名 */
    @Excel(name = "任務組名")
    private String jobGroup;

    /** 調用目標字符串 */
    @Excel(name = "調用目標字符串")
    private String invokeTarget;

    /** cron執行表達式 */
    @Excel(name = "執行表達式 ")
    private String cronExpression;

    /** cron計划策略 */
    @Excel(name = "計划策略 ", readConverterExp = "0=默認,1=立即觸發執行,2=觸發一次執行,3=不觸發立即執行")
    private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;

    /** 是否並發執行(0允許 1禁止) */
    @Excel(name = "並發執行", readConverterExp = "0=允許,1=禁止")
    private String concurrent;

    /** 任務狀態(0正常 1暫停) */
    @Excel(name = "任務狀態", readConverterExp = "0=正常,1=暫停")
    private String status;

    public Long getJobId()
    {
        return jobId;
    }

    public void setJobId(Long jobId)
    {
        this.jobId = jobId;
    }

    @NotBlank(message = "任務名稱不能為空")
    @Size(min = 0, max = 64, message = "任務名稱不能超過64個字符")
    public String getJobName()
    {
        return jobName;
    }

    public void setJobName(String jobName)
    {
        this.jobName = jobName;
    }

    public String getJobGroup()
    {
        return jobGroup;
    }

    public void setJobGroup(String jobGroup)
    {
        this.jobGroup = jobGroup;
    }

    @NotBlank(message = "調用目標字符串不能為空")
    @Size(min = 0, max = 500, message = "調用目標字符串長度不能超過500個字符")
    public String getInvokeTarget()
    {
        return invokeTarget;
    }

    public void setInvokeTarget(String invokeTarget)
    {
        this.invokeTarget = invokeTarget;
    }

    @NotBlank(message = "Cron執行表達式不能為空")
    @Size(min = 0, max = 255, message = "Cron執行表達式不能超過255個字符")
    public String getCronExpression()
    {
        return cronExpression;
    }

    public void setCronExpression(String cronExpression)
    {
        this.cronExpression = cronExpression;
    }

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    public Date getNextValidTime()
    {
        if (StringUtils.isNotEmpty(cronExpression))
        {
            return CronUtils.getNextExecution(cronExpression);
        }
        return null;
    }

    public String getMisfirePolicy()
    {
        return misfirePolicy;
    }

    public void setMisfirePolicy(String misfirePolicy)
    {
        this.misfirePolicy = misfirePolicy;
    }

    public String getConcurrent()
    {
        return concurrent;
    }

    public void setConcurrent(String concurrent)
    {
        this.concurrent = concurrent;
    }

    public String getStatus()
    {
        return status;
    }

    public void setStatus(String status)
    {
        this.status = status;
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
            .append("jobId", getJobId())
            .append("jobName", getJobName())
            .append("jobGroup", getJobGroup())
            .append("cronExpression", getCronExpression())
            .append("nextValidTime", getNextValidTime())
            .append("misfirePolicy", getMisfirePolicy())
            .append("concurrent", getConcurrent())
            .append("status", getStatus())
            .append("createBy", getCreateBy())
            .append("createTime", getCreateTime())
            .append("updateBy", getUpdateBy())
            .append("updateTime", getUpdateTime())
            .append("remark", getRemark())
            .toString();
    }
}

  任務日志實體類

package com.ruoyi.quartz.domain;

import java.util.Date;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;

/**
 * 定時任務調度日志表 sys_job_log
 * 
 * @author ruoyi
 */
public class SysJobLog extends BaseEntity
{
    private static final long serialVersionUID = 1L;

    /** ID */
    @Excel(name = "日志序號")
    private Long jobLogId;

    /** 任務名稱 */
    @Excel(name = "任務名稱")
    private String jobName;

    /** 任務組名 */
    @Excel(name = "任務組名")
    private String jobGroup;

    /** 調用目標字符串 */
    @Excel(name = "調用目標字符串")
    private String invokeTarget;

    /** 日志信息 */
    @Excel(name = "日志信息")
    private String jobMessage;

    /** 執行狀態(0正常 1失敗) */
    @Excel(name = "執行狀態", readConverterExp = "0=正常,1=失敗")
    private String status;

    /** 異常信息 */
    @Excel(name = "異常信息")
    private String exceptionInfo;

    /** 開始時間 */
    private Date startTime;

    /** 停止時間 */
    private Date stopTime;

    public Long getJobLogId()
    {
        return jobLogId;
    }

    public void setJobLogId(Long jobLogId)
    {
        this.jobLogId = jobLogId;
    }

    public String getJobName()
    {
        return jobName;
    }

    public void setJobName(String jobName)
    {
        this.jobName = jobName;
    }

    public String getJobGroup()
    {
        return jobGroup;
    }

    public void setJobGroup(String jobGroup)
    {
        this.jobGroup = jobGroup;
    }

    public String getInvokeTarget()
    {
        return invokeTarget;
    }

    public void setInvokeTarget(String invokeTarget)
    {
        this.invokeTarget = invokeTarget;
    }

    public String getJobMessage()
    {
        return jobMessage;
    }

    public void setJobMessage(String jobMessage)
    {
        this.jobMessage = jobMessage;
    }

    public String getStatus()
    {
        return status;
    }

    public void setStatus(String status)
    {
        this.status = status;
    }

    public String getExceptionInfo()
    {
        return exceptionInfo;
    }

    public void setExceptionInfo(String exceptionInfo)
    {
        this.exceptionInfo = exceptionInfo;
    }

    public Date getStartTime()
    {
        return startTime;
    }

    public void setStartTime(Date startTime)
    {
        this.startTime = startTime;
    }
    
    public Date getStopTime()
    {
        return stopTime;
    }

    public void setStopTime(Date stopTime)
    {
        this.stopTime = stopTime;
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
            .append("jobLogId", getJobLogId())
            .append("jobName", getJobName())
            .append("jobGroup", getJobGroup())
            .append("jobMessage", getJobMessage())
            .append("status", getStatus())
            .append("exceptionInfo", getExceptionInfo())
            .append("startTime", getStartTime())
            .append("stopTime", getStopTime())
            .toString();
    }
}

3.定時任務配置類

  配置實例化了SchedulerFactoryBean的工廠實例和名為RuoyiScheduler的Scheduler實例,且自動啟動

package com.ruoyi.quartz.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.sql.DataSource;
import java.util.Properties;

/**
 * 定時任務配置
 * 
 * @author ruoyi
 */
@Configuration
public class ScheduleConfig
{
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource)
    {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setDataSource(dataSource);

        // quartz參數
        Properties prop = new Properties();
        prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler");
        prop.put("org.quartz.scheduler.instanceId", "AUTO");
        // 線程池配置
        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        prop.put("org.quartz.threadPool.threadCount", "20");
        prop.put("org.quartz.threadPool.threadPriority", "5");
        // JobStore配置
        prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        // 集群配置
        prop.put("org.quartz.jobStore.isClustered", "true");
        prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
        prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
        prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");

        // sqlserver 啟用
        // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
        prop.put("org.quartz.jobStore.misfireThreshold", "12000");
        prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
        factory.setQuartzProperties(prop);

        factory.setSchedulerName("RuoyiScheduler");
        // 延時啟動
        factory.setStartupDelay(1);
        factory.setApplicationContextSchedulerContextKey("applicationContextKey");
        // 可選,QuartzScheduler
        // 啟動時更新己存在的Job,這樣就不用每次修改targetObject后刪除qrtz_job_details表對應記錄了
        factory.setOverwriteExistingJobs(true);
        // 設置自動啟動,默認為true
        factory.setAutoStartup(true);

        return factory;
    }
}

 

4.自定義JOB任務、定時任務處理

  JOB

package com.ruoyi.quartz.util;

import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.utils.ExceptionUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.domain.SysJobLog;
import com.ruoyi.quartz.service.ISysJobLogService;

/**
 * 抽象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<>();

    @Override
    public 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;
}

 

  定時任務處理-可並發

 

package com.ruoyi.quartz.util;

import org.quartz.JobExecutionContext;
import com.ruoyi.quartz.domain.SysJob;

/**
 * 定時任務處理(允許並發執行)
 * 
 * @author ruoyi
 *
 */
public class QuartzJobExecution extends AbstractQuartzJob
{
    @Override
    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception
    {
        JobInvokeUtil.invokeMethod(sysJob);  //自己重寫了執行的方法  因為要傳遞參數
    }
}


  定時任務處理-不可並發

  

package com.ruoyi.quartz.util;

import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import com.ruoyi.quartz.domain.SysJob;

/**
 * 定時任務處理(禁止並發執行)
 * 
 * @author ruoyi
 *
 */
@DisallowConcurrentExecution
public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob
{
    @Override
    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception
    {
        JobInvokeUtil.invokeMethod(sysJob);
    }
}

  任務執行工具

  

package com.ruoyi.quartz.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.quartz.domain.SysJob;

/**
 * 任務執行工具 -- 自己通過反射區獲取定義的任務的方法去執行 --- 因為需要傳遞參數
 *
 * @author ruoyi
 */
public class JobInvokeUtil
{
    /**
     * 執行方法
     *
     * @param sysJob 系統任務
     */
    public static void invokeMethod(SysJob sysJob) throws Exception
    {
        String invokeTarget = sysJob.getInvokeTarget();  //如:ryTask.ryMultipleParams('ry', true, 2000L, 316.50D, 100)
        String beanName = getBeanName(invokeTarget); //如:ryTask
        String methodName = getMethodName(invokeTarget); //如ryMultipleParams
        List<Object[]> methodParams = getMethodParams(invokeTarget); //數組里面存的是參數值和參數類型

        if (!isValidClassName(beanName)) //ryTask   判斷是否是全類名,如果是,去掉包名
        {
            Object bean = SpringUtils.getBean(beanName);//獲取具體任務對象
            invokeMethod(bean, methodName, methodParams);
        }
        else
        {
            Object bean = Class.forName(beanName).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().getDeclaredMethod(methodName, getMethodParamsType(methodParams));//通過反射獲取方法對象
            method.invoke(bean, getMethodParamsValue(methodParams));//執行方法
        }
        else
        {
            Method method = bean.getClass().getDeclaredMethod(methodName);
            method.invoke(bean);
        }
    }

    /**
     * 校驗是否為為class包名
     * 
     * @param str 名稱
     * @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)//ryTask.ryMultipleParams('ry', true, 2000L, 316.50D, 100)
    {
        String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");//'ry', true, 2000L, 316.50D, 100
        if (StringUtils.isEmpty(methodStr))
        {
            return null;
        }
        String[] methodParams = methodStr.split(",");//['ry',  true,  2000L,  316.50D,  100]
        List<Object[]> classs = new LinkedList<>();//存儲參數值和參數類型
        for (int i = 0; i < methodParams.length; i++)
        {
            String str = StringUtils.trimToEmpty(methodParams[i]);//'ry'
            // String字符串類型,包含'
            if (StringUtils.contains(str, "'"))
            {
                classs.add(new Object[] { StringUtils.replace(str, "'", ""), String.class });
            }
            // boolean布爾類型,等於true或者false
            else if (StringUtils.equals(str, "true") || StringUtils.equalsIgnoreCase(str, "false"))
            {
                classs.add(new Object[] { Boolean.valueOf(str), Boolean.class });
            }
            // long長整形,包含L
            else if (StringUtils.containsIgnoreCase(str, "L"))
            {
                classs.add(new Object[] { Long.valueOf(StringUtils.replaceIgnoreCase(str, "L", "")), Long.class });
            }
            // double浮點類型,包含D
            else if (StringUtils.containsIgnoreCase(str, "D"))
            {
                classs.add(new Object[] { Double.valueOf(StringUtils.replaceIgnoreCase(str, "D", "")), 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;
    }
}

  定時任務工具類

  

package com.ruoyi.quartz.util;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.common.exception.job.TaskException.Code;
import com.ruoyi.quartz.domain.SysJob;

/**
 * 定時任務工具類
 * 
 * @author ruoyi
 *
 */
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(); //創建了一個 JobDetail,類型為jobClass,並設置了Jobkey-就是這個Job的標識

        // 表達式調度構建器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); //job.getCronExpression()任務調度表達式如0/30 * * * * ?
        cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder); //設置定時任務策略

        // 按新的cronExpression表達式構建一個新的trigger
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup)) //創建了一個觸發器-並設置了TriggerKey-就是Trigger的標識
                .withSchedule(cronScheduleBuilder).build();

        // 放入參數,運行時的方法可以獲取
        jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job); //在任務對象中插入我們自定義的(數據庫里的)Job對象-把消息儲存起來

        // 判斷是否存在
        if (scheduler.checkExists(getJobKey(jobId, jobGroup)))
        {
            // 防止創建時存在數據問題 先移除,然后在執行創建操作
            scheduler.deleteJob(getJobKey(jobId, jobGroup));
        }

        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);
        }
    }
}

5.Mapper接口

package com.ruoyi.quartz.mapper;

import java.util.List;
import com.ruoyi.quartz.domain.SysJob;

/**
 * 調度任務信息 數據層
 * 
 * @author ruoyi
 */
public interface SysJobMapper
{
    /**
     * 查詢調度任務日志集合
     * 
     * @param job 調度信息
     * @return 操作日志集合
     */
    public List<SysJob> selectJobList(SysJob job);

    /**
     * 查詢所有調度任務
     * 
     * @return 調度任務列表
     */
    public List<SysJob> selectJobAll();

    /**
     * 通過調度ID查詢調度任務信息
     * 
     * @param jobId 調度ID
     * @return 角色對象信息
     */
    public SysJob selectJobById(Long jobId);

    /**
     * 通過調度ID刪除調度任務信息
     * 
     * @param jobId 調度ID
     * @return 結果
     */
    public int deleteJobById(Long jobId);

    /**
     * 批量刪除調度任務信息
     * 
     * @param ids 需要刪除的數據ID
     * @return 結果
     */
    public int deleteJobByIds(Long[] ids);

    /**
     * 修改調度任務信息
     * 
     * @param job 調度任務信息
     * @return 結果
     */
    public int updateJob(SysJob job);

    /**
     * 新增調度任務信息
     * 
     * @param job 調度任務信息
     * @return 結果
     */
    public int insertJob(SysJob job);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.quartz.mapper.SysJobMapper">

    <resultMap type="SysJob" id="SysJobResult">
        <id     property="jobId"          column="job_id"          />
        <result property="jobName"        column="job_name"        />
        <result property="jobGroup"       column="job_group"       />
        <result property="invokeTarget"   column="invoke_target"   />
        <result property="cronExpression" column="cron_expression" />
        <result property="misfirePolicy"  column="misfire_policy"  />
        <result property="concurrent"     column="concurrent"      />
        <result property="status"         column="status"          />
        <result property="createBy"       column="create_by"       />
        <result property="createTime"     column="create_time"     />
        <result property="updateBy"       column="update_by"       />
        <result property="updateTime"     column="update_time"     />
        <result property="remark"         column="remark"          />
    </resultMap>
    
    <sql id="selectJobVo">
        select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark 
        from sys_job
    </sql>
    
    <select id="selectJobList" parameterType="SysJob" resultMap="SysJobResult">
        <include refid="selectJobVo"/>
        <where>
            <if test="jobName != null and jobName != ''">
                AND job_name like concat('%', #{jobName}, '%')
            </if>
            <if test="jobGroup != null and jobGroup != ''">
                AND job_group = #{jobGroup}
            </if>
            <if test="status != null and status != ''">
                AND status = #{status}
            </if>
            <if test="invokeTarget != null and invokeTarget != ''">
                AND invoke_target like concat('%', #{invokeTarget}, '%')
            </if>
        </where>
    </select>
    
    <select id="selectJobAll" resultMap="SysJobResult">
        <include refid="selectJobVo"/>
    </select>
    
    <select id="selectJobById" parameterType="Long" resultMap="SysJobResult">
        <include refid="selectJobVo"/>
        where job_id = #{jobId}
    </select>
    
    <delete id="deleteJobById" parameterType="Long">
         delete from sys_job where job_id = #{jobId}
     </delete>
     
     <delete id="deleteJobByIds" parameterType="Long">
         delete from sys_job where job_id in
         <foreach collection="array" item="jobId" open="(" separator="," close=")">
             #{jobId}
        </foreach> 
     </delete>
     
     <update id="updateJob" parameterType="SysJob">
         update sys_job
         <set>
             <if test="jobName != null and jobName != ''">job_name = #{jobName},</if>
             <if test="jobGroup != null and jobGroup != ''">job_group = #{jobGroup},</if>
             <if test="invokeTarget != null and invokeTarget != ''">invoke_target = #{invokeTarget},</if>
             <if test="cronExpression != null and cronExpression != ''">cron_expression = #{cronExpression},</if>
             <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy = #{misfirePolicy},</if>
             <if test="concurrent != null and concurrent != ''">concurrent = #{concurrent},</if>
             <if test="status !=null">status = #{status},</if>
             <if test="remark != null and remark != ''">remark = #{remark},</if>
             <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
             update_time = sysdate()
         </set>
         where job_id = #{jobId}
    </update>
     
     <insert id="insertJob" parameterType="SysJob" useGeneratedKeys="true" keyProperty="jobId">
         insert into sys_job(
             <if test="jobId != null and jobId != 0">job_id,</if>
             <if test="jobName != null and jobName != ''">job_name,</if>
             <if test="jobGroup != null and jobGroup != ''">job_group,</if>
             <if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if>
             <if test="cronExpression != null and cronExpression != ''">cron_expression,</if>
             <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy,</if>
             <if test="concurrent != null and concurrent != ''">concurrent,</if>
             <if test="status != null and status != ''">status,</if>
             <if test="remark != null and remark != ''">remark,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
             create_time
         )values(
             <if test="jobId != null and jobId != 0">#{jobId},</if>
             <if test="jobName != null and jobName != ''">#{jobName},</if>
             <if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if>
             <if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if>
             <if test="cronExpression != null and cronExpression != ''">#{cronExpression},</if>
             <if test="misfirePolicy != null and misfirePolicy != ''">#{misfirePolicy},</if>
             <if test="concurrent != null and concurrent != ''">#{concurrent},</if>
             <if test="status != null and status != ''">#{status},</if>
             <if test="remark != null and remark != ''">#{remark},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
             sysdate()
         )
    </insert>

</mapper> 

6.任務調度常量

  

package com.ruoyi.common.constant;

/**
 * 任務調度通用常量
 * 
 * @author ruoyi
 */
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 String value;

        private Status(String value)
        {
            this.value = value;
        }

        public String getValue()
        {
            return value;
        }
    }
}

 

7.定時任務測試

package com.ruoyi.quartz.task;

import org.springframework.stereotype.Component;
import com.ruoyi.common.utils.StringUtils;

/**
 * 定時任務調度測試
 * 
 * @author ruoyi
 */
@Component("ryTask")
public class RyTask
{
    public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i)
    {
        System.out.println(StringUtils.format("執行多參方法: 字符串類型{},布爾類型{},長整型{},浮點型{},整形{}", s, b, l, d, i));
    }

    public void ryParams(String params)
    {
        System.out.println("執行有參方法:" + params);
    }

    public void ryNoParams()
    {
        System.out.println("執行無參方法");
    }
}

 

package com.ruoyi.quartz.controller;

import java.util.List;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.service.ISysJobService;
import com.ruoyi.quartz.util.CronUtils;

/**
 * 調度任務信息操作處理
 * 
 * @author ruoyi
 */
@RestController
@RequestMapping("/monitor/job")
public class SysJobController extends BaseController
{
    @Autowired
    private ISysJobService jobService;

    /**
     * 查詢定時任務列表
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysJob sysJob)
    {
        startPage();
        List<SysJob> list = jobService.selectJobList(sysJob);
        return getDataTable(list);
    }

  /**
     * 獲取定時任務詳細信息
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:query')")
    @GetMapping(value = "/{jobId}")
    public AjaxResult getInfo(@PathVariable("jobId") Long jobId)
    {
        return AjaxResult.success(jobService.selectJobById(jobId));
    }

    /**
     * 新增定時任務
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:add')")

    @PostMapping
    public AjaxResult add(@RequestBody SysJob sysJob) throws SchedulerException, TaskException
    {
        if (!CronUtils.isValid(sysJob.getCronExpression()))
        {
            return AjaxResult.error("cron表達式不正確");
        }
       
        return toAjax(jobService.insertJob(sysJob));
    }

    /**
     * 修改定時任務
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:edit')")

    @PutMapping
    public AjaxResult edit(@RequestBody SysJob sysJob) throws SchedulerException, TaskException
    {
        if (!CronUtils.isValid(sysJob.getCronExpression()))
        {
            return AjaxResult.error("cron表達式不正確");
        }
        
        return toAjax(jobService.updateJob(sysJob));
    }

    /**
     * 定時任務狀態修改
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
    
    @PutMapping("/changeStatus")
    public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException
    {
        SysJob newJob = jobService.selectJobById(job.getJobId());
        newJob.setStatus(job.getStatus());
        return toAjax(jobService.changeStatus(newJob));
    }

    /**
     * 定時任務立即執行一次
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
    
    @PutMapping("/run")
    public AjaxResult run(@RequestBody SysJob job) throws SchedulerException
    {
        jobService.run(job);
        return AjaxResult.success();
    }

    /**
     * 刪除定時任務
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
    
    @DeleteMapping("/{jobIds}")
    public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException
    {
        jobService.deleteJobByIds(jobIds);
        return AjaxResult.success();
    }
}
package com.ruoyi.quartz.service;

import java.util.List;
import org.quartz.SchedulerException;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.quartz.domain.SysJob;

/**
 * 定時任務調度信息信息 服務層
 * 
 * @author ruoyi
 */
public interface ISysJobService
{
    /**
     * 獲取quartz調度器的計划任務
     * 
     * @param job 調度信息
     * @return 調度任務集合
     */
    public List<SysJob> selectJobList(SysJob job);

    /**
     * 通過調度任務ID查詢調度信息
     * 
     * @param jobId 調度任務ID
     * @return 調度任務對象信息
     */
    public SysJob selectJobById(Long jobId);

    /**
     * 暫停任務
     * 
     * @param job 調度信息
     * @return 結果
     */
    public int pauseJob(SysJob job) throws SchedulerException;

    /**
     * 恢復任務
     * 
     * @param job 調度信息
     * @return 結果
     */
    public int resumeJob(SysJob job) throws SchedulerException;

    /**
     * 刪除任務后,所對應的trigger也將被刪除
     * 
     * @param job 調度信息
     * @return 結果
     */
    public int deleteJob(SysJob job) throws SchedulerException;

    /**
     * 批量刪除調度信息
     * 
     * @param jobIds 需要刪除的任務ID
     * @return 結果
     */
    public void deleteJobByIds(Long[] jobIds) throws SchedulerException;

    /**
     * 任務調度狀態修改
     * 
     * @param job 調度信息
     * @return 結果
     */
    public int changeStatus(SysJob job) throws SchedulerException;

    /**
     * 立即運行任務
     * 
     * @param job 調度信息
     * @return 結果
     */
    public void run(SysJob job) throws SchedulerException;

    /**
     * 新增任務
     * 
     * @param job 調度信息
     * @return 結果
     */
    public int insertJob(SysJob job) throws SchedulerException, TaskException;

    /**
     * 更新任務
     * 
     * @param job 調度信息
     * @return 結果
     */
    public int updateJob(SysJob job) throws SchedulerException, TaskException;

    /**
     * 校驗cron表達式是否有效
     * 
     * @param cronExpression 表達式
     * @return 結果
     */
    public boolean checkCronExpressionIsValid(String cronExpression);
}
package com.ruoyi.quartz.service.impl;

import java.util.List;
import javax.annotation.PostConstruct;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.mapper.SysJobMapper;
import com.ruoyi.quartz.service.ISysJobService;
import com.ruoyi.quartz.util.CronUtils;
import com.ruoyi.quartz.util.ScheduleUtils;

/**
 * 定時任務調度信息 服務層
 * 
 * @author ruoyi
 */
@Service
public class SysJobServiceImpl implements ISysJobService
{
    @Autowired
    private Scheduler scheduler;

    @Autowired
    private SysJobMapper jobMapper;

    /**
     * 項目啟動時,初始化定時器 主要是防止手動修改數據庫導致未同步到定時任務處理(注:不能手動修改數據庫ID和任務組名,否則會導致臟數據)
     */
    @PostConstruct
    public void init() throws SchedulerException, TaskException
    {
        scheduler.clear();
        List<SysJob> jobList = jobMapper.selectJobAll();
        for (SysJob job : jobList)
        {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
    }

    /**
     * 獲取quartz調度器的計划任務列表
     * 
     * @param job 調度信息
     * @return
     */
    @Override
    public List<SysJob> selectJobList(SysJob job)
    {
        return jobMapper.selectJobList(job);
    }

    /**
     * 通過調度任務ID查詢調度信息
     * 
     * @param jobId 調度任務ID
     * @return 調度任務對象信息
     */
    @Override
    public SysJob selectJobById(Long jobId)
    {
        return jobMapper.selectJobById(jobId);
    }

    /**
     * 暫停任務
     * 
     * @param job 調度信息
     */
    @Override
    @Transactional
    public int pauseJob(SysJob job) throws SchedulerException
    {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        int rows = jobMapper.updateJob(job);
        if (rows > 0)
        {
            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows;
    }

    /**
     * 恢復任務
     * 
     * @param job 調度信息
     */
    @Override
    @Transactional
    public int resumeJob(SysJob job) throws SchedulerException
    {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
        int rows = jobMapper.updateJob(job);
        if (rows > 0)
        {
            scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows;
    }

    /**
     * 刪除任務后,所對應的trigger也將被刪除
     * 
     * @param job 調度信息
     */
    @Override
    @Transactional
    public int deleteJob(SysJob job) throws SchedulerException
    {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        int rows = jobMapper.deleteJobById(jobId);
        if (rows > 0)
        {
            scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows;
    }

    /**
     * 批量刪除調度信息
     * 
     * @param jobIds 需要刪除的任務ID
     * @return 結果
     */
    @Override
    @Transactional
    public void deleteJobByIds(Long[] jobIds) throws SchedulerException
    {
        for (Long jobId : jobIds)
        {
            SysJob job = jobMapper.selectJobById(jobId);
            deleteJob(job);
        }
    }

    /**
     * 任務調度狀態修改
     * 
     * @param job 調度信息
     */
    @Override
    @Transactional
    public int changeStatus(SysJob job) throws SchedulerException
    {
        int rows = 0;
        String status = job.getStatus();
        if (ScheduleConstants.Status.NORMAL.getValue().equals(status))
        {
            rows = resumeJob(job);
        }
        else if (ScheduleConstants.Status.PAUSE.getValue().equals(status))
        {
            rows = pauseJob(job);
        }
        return rows;
    }

    /**
     * 立即運行任務
     * 
     * @param job 調度信息
     */
    @Override
    @Transactional
    public void run(SysJob job) throws SchedulerException
    {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        SysJob properties = selectJobById(job.getJobId());
        // 參數
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
        System.out.println(scheduler.getSchedulerName());
        scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap);  //執行一次
    }

    /**
     * 新增任務
     * 
     * @param job 調度信息 調度信息
     */
    @Override
    @Transactional
    public int insertJob(SysJob job) throws SchedulerException, TaskException
    {
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        int rows = jobMapper.insertJob(job);
        if (rows > 0)
        {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
        return rows;
    }

    /**
     * 更新任務的時間表達式
     * 
     * @param job 調度信息
     */
    @Override
    @Transactional
    public int updateJob(SysJob job) throws SchedulerException, TaskException
    {
        SysJob properties = selectJobById(job.getJobId());
        int rows = jobMapper.updateJob(job);
        if (rows > 0)
        {
            updateSchedulerJob(job, properties.getJobGroup());
        }
        return rows;
    }

    /**
     * 更新任務
     * 
     * @param job 任務對象
     * @param jobGroup 任務組名
     */
    public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException
    {
        Long jobId = job.getJobId();
        // 判斷是否存在
        JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey))
        {
            // 防止創建時存在數據問題 先移除,然后在執行創建操作
            scheduler.deleteJob(jobKey);
        }
        ScheduleUtils.createScheduleJob(scheduler, job);
    }

    /**
     * 校驗cron表達式是否有效
     * 
     * @param cronExpression 表達式
     * @return 結果
     */
    @Override
    public boolean checkCronExpressionIsValid(String cronExpression)
    {
        return CronUtils.isValid(cronExpression);
    }
}

8.工具類

package com.ruoyi.quartz.util;

import java.text.ParseException;
import java.util.Date;
import org.quartz.CronExpression;

/**
 * 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());
        }
    }
}
package com.ruoyi.common.constant;

/**
 * 通用常量信息
 * 
 * @author ruoyi
 */
public class Constants
{
    /**
     * UTF-8 字符集
     */
    public static final String UTF8 = "UTF-8";

    /**
     * GBK 字符集
     */
    public static final String GBK = "GBK";

    /**
     * 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";

    /**
     * 登錄成功
     */
    public static final String LOGIN_SUCCESS = "Success";

    /**
     * 注銷
     */
    public static final String LOGOUT = "Logout";

    /**
     * 登錄失敗
     */
    public static final String LOGIN_FAIL = "Error";

    /**
     * 驗證碼 redis key
     */
    public static final String CAPTCHA_CODE_KEY = "captcha_codes:";

    /**
     * 登錄用戶 redis key
     */
    public static final String LOGIN_TOKEN_KEY = "login_tokens:";
    
    /**
     * 防重提交 redis key
     */
    public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";

    /**
     * 驗證碼有效期(分鍾)
     */
    public static final Integer CAPTCHA_EXPIRATION = 2;

    /**
     * 令牌
     */
    public static final String TOKEN = "token";

    /**
     * 令牌前綴
     */
    public static final String TOKEN_PREFIX = "Bearer ";

    /**
     * 令牌前綴
     */
    public static final String LOGIN_USER_KEY = "login_user_key";

    /**
     * 用戶ID
     */
    public static final String JWT_USERID = "userid";

    /**
     * 用戶名稱
     */
    public static final String JWT_USERNAME = "sub";

    /**
     * 用戶頭像
     */
    public static final String JWT_AVATAR = "avatar";

    /**
     * 創建時間
     */
    public static final String JWT_CREATED = "created";

    /**
     * 用戶權限
     */
    public static final String JWT_AUTHORITIES = "authorities";

    /**
     * 參數管理 cache key
     */
    public static final String SYS_CONFIG_KEY = "sys_config:";

    /**
     * 字典管理 cache key
     */
    public static final String SYS_DICT_KEY = "sys_dict:";

    /**
     * 資源映射路徑 前綴
     */
    public static final String RESOURCE_PREFIX = "/profile";
}
package com.ruoyi.common.core.controller;

import java.beans.PropertyEditorSupport;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.sql.SqlUtil;

/**
 * web層通用數據處理
 * 
 * @author ruoyi
 */
public class BaseController
{
    protected final Logger logger = LoggerFactory.getLogger(BaseController.class);

    /**
     * 將前台傳遞過來的日期格式的字符串,自動轉化為Date類型
     */
    @InitBinder
    public void initBinder(WebDataBinder binder)
    {
        // Date 類型轉換
        binder.registerCustomEditor(Date.class, new PropertyEditorSupport()
        {
            @Override
            public void setAsText(String text)
            {
                setValue(DateUtils.parseDate(text));
            }
        });
    }

    /**
     * 設置請求分頁數據
     */
    protected void startPage()
    {
        PageDomain pageDomain = TableSupport.buildPageRequest();
        Integer pageNum = pageDomain.getPageNum();
        Integer pageSize = pageDomain.getPageSize();
        if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize))
        {
            String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
            PageHelper.startPage(pageNum, pageSize, orderBy);
        }
    }

    /**
     * 響應請求分頁數據
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected TableDataInfo getDataTable(List<?> list)
    {
        TableDataInfo rspData = new TableDataInfo();
        rspData.setCode(HttpStatus.SUCCESS);
        rspData.setMsg("查詢成功");
        rspData.setRows(list);
        rspData.setTotal(new PageInfo(list).getTotal());
        return rspData;
    }

    /**
     * 響應返回結果
     * 
     * @param rows 影響行數
     * @return 操作結果
     */
    protected AjaxResult toAjax(int rows)
    {
        return rows > 0 ? AjaxResult.success() : AjaxResult.error();
    }

    /**
     * 頁面跳轉
     */
    public String redirect(String url)
    {
        return StringUtils.format("redirect:{}", url);
    }
}
package com.ruoyi.common.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.ruoyi.common.core.text.StrFormatter;

/**
 * 字符串工具類
 * 
 * @author ruoyi
 */
public class StringUtils extends org.apache.commons.lang3.StringUtils
{
    /** 空字符串 */
    private static final String NULLSTR = "";

    /** 下划線 */
    private static final char SEPARATOR = '_';

    /**
     * 獲取參數不為空值
     * 
     * @param value defaultValue 要判斷的value
     * @return value 返回值
     */
    public static <T> T nvl(T value, T defaultValue)
    {
        return value != null ? value : defaultValue;
    }

    /**
     * * 判斷一個Collection是否為空, 包含List,Set,Queue
     * 
     * @param coll 要判斷的Collection
     * @return true:為空 false:非空
     */
    public static boolean isEmpty(Collection<?> coll)
    {
        return isNull(coll) || coll.isEmpty();
    }

    /**
     * * 判斷一個Collection是否非空,包含List,Set,Queue
     * 
     * @param coll 要判斷的Collection
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Collection<?> coll)
    {
        return !isEmpty(coll);
    }

    /**
     * * 判斷一個對象數組是否為空
     * 
     * @param objects 要判斷的對象數組
     ** @return true:為空 false:非空
     */
    public static boolean isEmpty(Object[] objects)
    {
        return isNull(objects) || (objects.length == 0);
    }

    /**
     * * 判斷一個對象數組是否非空
     * 
     * @param objects 要判斷的對象數組
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Object[] objects)
    {
        return !isEmpty(objects);
    }

    /**
     * * 判斷一個Map是否為空
     * 
     * @param map 要判斷的Map
     * @return true:為空 false:非空
     */
    public static boolean isEmpty(Map<?, ?> map)
    {
        return isNull(map) || map.isEmpty();
    }

    /**
     * * 判斷一個Map是否為空
     * 
     * @param map 要判斷的Map
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Map<?, ?> map)
    {
        return !isEmpty(map);
    }

    /**
     * * 判斷一個字符串是否為空串
     * 
     * @param str String
     * @return true:為空 false:非空
     */
    public static boolean isEmpty(String str)
    {
        return isNull(str) || NULLSTR.equals(str.trim());
    }

    /**
     * * 判斷一個字符串是否為非空串
     * 
     * @param str String
     * @return true:非空串 false:空串
     */
    public static boolean isNotEmpty(String str)
    {
        return !isEmpty(str);
    }

    /**
     * * 判斷一個對象是否為空
     * 
     * @param object Object
     * @return true:為空 false:非空
     */
    public static boolean isNull(Object object)
    {
        return object == null;
    }

    /**
     * * 判斷一個對象是否非空
     * 
     * @param object Object
     * @return true:非空 false:空
     */
    public static boolean isNotNull(Object object)
    {
        return !isNull(object);
    }

    /**
     * * 判斷一個對象是否是數組類型(Java基本型別的數組)
     * 
     * @param object 對象
     * @return true:是數組 false:不是數組
     */
    public static boolean isArray(Object object)
    {
        return isNotNull(object) && object.getClass().isArray();
    }

    /**
     * 去空格
     */
    public static String trim(String str)
    {
        return (str == null ? "" : str.trim());
    }

    /**
     * 截取字符串
     * 
     * @param str 字符串
     * @param start 開始
     * @return 結果
     */
    public static String substring(final String str, int start)
    {
        if (str == null)
        {
            return NULLSTR;
        }

        if (start < 0)
        {
            start = str.length() + start;
        }

        if (start < 0)
        {
            start = 0;
        }
        if (start > str.length())
        {
            return NULLSTR;
        }

        return str.substring(start);
    }

    /**
     * 截取字符串
     * 
     * @param str 字符串
     * @param start 開始
     * @param end 結束
     * @return 結果
     */
    public static String substring(final String str, int start, int end)
    {
        if (str == null)
        {
            return NULLSTR;
        }

        if (end < 0)
        {
            end = str.length() + end;
        }
        if (start < 0)
        {
            start = str.length() + start;
        }

        if (end > str.length())
        {
            end = str.length();
        }

        if (start > end)
        {
            return NULLSTR;
        }

        if (start < 0)
        {
            start = 0;
        }
        if (end < 0)
        {
            end = 0;
        }

        return str.substring(start, end);
    }

    /**
     * 格式化文本, {} 表示占位符<br>
     * 此方法只是簡單將占位符 {} 按照順序替換為參數<br>
     * 如果想輸出 {} 使用 \\轉義 { 即可,如果想輸出 {} 之前的 \ 使用雙轉義符 \\\\ 即可<br>
     * 例:<br>
     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
     * 轉義{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
     * 轉義\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
     * 
     * @param template 文本模板,被替換的部分用 {} 表示
     * @param params 參數值
     * @return 格式化后的文本
     */
    public static String format(String template, Object... params)
    {
        if (isEmpty(params) || isEmpty(template))
        {
            return template;
        }
        return StrFormatter.format(template, params);
    }

    /**
     * 字符串轉set
     * 
     * @param str 字符串
     * @param sep 分隔符
     * @return set集合
     */
    public static final Set<String> str2Set(String str, String sep)
    {
        return new HashSet<String>(str2List(str, sep, true, false));
    }

    /**
     * 字符串轉list
     * 
     * @param str 字符串
     * @param sep 分隔符
     * @param filterBlank 過濾純空白
     * @param trim 去掉首尾空白
     * @return list集合
     */
    public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim)
    {
        List<String> list = new ArrayList<String>();
        if (StringUtils.isEmpty(str))
        {
            return list;
        }

        // 過濾空白字符串
        if (filterBlank && StringUtils.isBlank(str))
        {
            return list;
        }
        String[] split = str.split(sep);
        for (String string : split)
        {
            if (filterBlank && StringUtils.isBlank(string))
            {
                continue;
            }
            if (trim)
            {
                string = string.trim();
            }
            list.add(string);
        }

        return list;
    }

    /**
     * 下划線轉駝峰命名
     */
    public static String toUnderScoreCase(String str)
    {
        if (str == null)
        {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        // 前置字符是否大寫
        boolean preCharIsUpperCase = true;
        // 當前字符是否大寫
        boolean curreCharIsUpperCase = true;
        // 下一字符是否大寫
        boolean nexteCharIsUpperCase = true;
        for (int i = 0; i < str.length(); i++)
        {
            char c = str.charAt(i);
            if (i > 0)
            {
                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
            }
            else
            {
                preCharIsUpperCase = false;
            }

            curreCharIsUpperCase = Character.isUpperCase(c);

            if (i < (str.length() - 1))
            {
                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
            }

            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
            {
                sb.append(SEPARATOR);
            }
            else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
            {
                sb.append(SEPARATOR);
            }
            sb.append(Character.toLowerCase(c));
        }

        return sb.toString();
    }

    /**
     * 是否包含字符串
     * 
     * @param str 驗證字符串
     * @param strs 字符串組
     * @return 包含返回true
     */
    public static boolean inStringIgnoreCase(String str, String... strs)
    {
        if (str != null && strs != null)
        {
            for (String s : strs)
            {
                if (str.equalsIgnoreCase(trim(s)))
                {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 將下划線大寫方式命名的字符串轉換為駝峰式。如果轉換前的下划線大寫方式命名的字符串為空,則返回空字符串。 例如:HELLO_WORLD->HelloWorld
     * 
     * @param name 轉換前的下划線大寫方式命名的字符串
     * @return 轉換后的駝峰式命名的字符串
     */
    public static String convertToCamelCase(String name)
    {
        StringBuilder result = new StringBuilder();
        // 快速檢查
        if (name == null || name.isEmpty())
        {
            // 沒必要轉換
            return "";
        }
        else if (!name.contains("_"))
        {
            // 不含下划線,僅將首字母大寫
            return name.substring(0, 1).toUpperCase() + name.substring(1);
        }
        // 用下划線將原始字符串分割
        String[] camels = name.split("_");
        for (String camel : camels)
        {
            // 跳過原始字符串中開頭、結尾的下換線或雙重下划線
            if (camel.isEmpty())
            {
                continue;
            }
            // 首字母大寫
            result.append(camel.substring(0, 1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        return result.toString();
    }

    /**
     * 駝峰式命名法 例如:user_name->userName
     */
    public static String toCamelCase(String s)
    {
        if (s == null)
        {
            return null;
        }
        s = s.toLowerCase();
        StringBuilder sb = new StringBuilder(s.length());
        boolean upperCase = false;
        for (int i = 0; i < s.length(); i++)
        {
            char c = s.charAt(i);

            if (c == SEPARATOR)
            {
                upperCase = true;
            }
            else if (upperCase)
            {
                sb.append(Character.toUpperCase(c));
                upperCase = false;
            }
            else
            {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    @SuppressWarnings("unchecked")
    public static <T> T cast(Object obj)
    {
        return (T) obj;
    }
}
package com.ruoyi.common.utils.sql;

import com.ruoyi.common.exception.BaseException;
import com.ruoyi.common.utils.StringUtils;

/**
 * sql操作工具類
 * 
 * @author ruoyi
 */
public class SqlUtil
{
    /**
     * 僅支持字母、數字、下划線、空格、逗號、小數點(支持多個字段排序)
     */
    public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";

    /**
     * 檢查字符,防止注入繞過
     */
    public static String escapeOrderBySql(String value)
    {
        if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value))
        {
            throw new BaseException("參數不符合規范,不能進行查詢");
        }
        return value;
    }

    /**
     * 驗證 order by 語法是否符合規范
     */
    public static boolean isValidOrderBySql(String value)
    {
        return value.matches(SQL_PATTERN);
    }
}
package com.ruoyi.common.core.domain;

import java.util.HashMap;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.utils.StringUtils;

/**
 * 操作消息提醒
 * 
 * @author ruoyi
 */
public class AjaxResult extends HashMap<String, Object>
{
    private static final long serialVersionUID = 1L;

    /** 狀態碼 */
    public static final String CODE_TAG = "code";

    /** 返回內容 */
    public static final String MSG_TAG = "msg";

    /** 數據對象 */
    public static final String DATA_TAG = "data";

    /**
     * 初始化一個新創建的 AjaxResult 對象,使其表示一個空消息。
     */
    public AjaxResult()
    {
    }

    /**
     * 初始化一個新創建的 AjaxResult 對象
     * 
     * @param code 狀態碼
     * @param msg 返回內容
     */
    public AjaxResult(int code, String msg)
    {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
    }

    /**
     * 初始化一個新創建的 AjaxResult 對象
     * 
     * @param code 狀態碼
     * @param msg 返回內容
     * @param data 數據對象
     */
    public AjaxResult(int code, String msg, Object data)
    {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
        if (StringUtils.isNotNull(data))
        {
            super.put(DATA_TAG, data);
        }
    }

    /**
     * 返回成功消息
     * 
     * @return 成功消息
     */
    public static AjaxResult success()
    {
        return AjaxResult.success("操作成功");
    }

    /**
     * 返回成功數據
     * 
     * @return 成功消息
     */
    public static AjaxResult success(Object data)
    {
        return AjaxResult.success("操作成功", data);
    }

    /**
     * 返回成功消息
     * 
     * @param msg 返回內容
     * @return 成功消息
     */
    public static AjaxResult success(String msg)
    {
        return AjaxResult.success(msg, null);
    }

    /**
     * 返回成功消息
     * 
     * @param msg 返回內容
     * @param data 數據對象
     * @return 成功消息
     */
    public static AjaxResult success(String msg, Object data)
    {
        return new AjaxResult(HttpStatus.SUCCESS, msg, data);
    }

    /**
     * 返回錯誤消息
     * 
     * @return
     */
    public static AjaxResult error()
    {
        return AjaxResult.error("操作失敗");
    }

    /**
     * 返回錯誤消息
     * 
     * @param msg 返回內容
     * @return 警告消息
     */
    public static AjaxResult error(String msg)
    {
        return AjaxResult.error(msg, null);
    }

    /**
     * 返回錯誤消息
     * 
     * @param msg 返回內容
     * @param data 數據對象
     * @return 警告消息
     */
    public static AjaxResult error(String msg, Object data)
    {
        return new AjaxResult(HttpStatus.ERROR, msg, data);
    }

    /**
     * 返回錯誤消息
     * 
     * @param code 狀態碼
     * @param msg 返回內容
     * @return 警告消息
     */
    public static AjaxResult error(int code, String msg)
    {
        return new AjaxResult(code, msg, null);
    }
}
package com.ruoyi.common.core.page;

import java.io.Serializable;
import java.util.List;

/**
 * 表格分頁數據對象
 * 
 * @author ruoyi
 */
public class TableDataInfo implements Serializable
{
    private static final long serialVersionUID = 1L;

    /** 總記錄數 */
    private long total;

    /** 列表數據 */
    private List<?> rows;

    /** 消息狀態碼 */
    private int code;

    /** 消息內容 */
    private String msg;

    /**
     * 表格數據對象
     */
    public TableDataInfo()
    {
    }

    /**
     * 分頁
     * 
     * @param list 列表數據
     * @param total 總記錄數
     */
    public TableDataInfo(List<?> list, int total)
    {
        this.rows = list;
        this.total = total;
    }

    public long getTotal()
    {
        return total;
    }

    public void setTotal(long total)
    {
        this.total = total;
    }

    public List<?> getRows()
    {
        return rows;
    }

    public void setRows(List<?> rows)
    {
        this.rows = rows;
    }

    public int getCode()
    {
        return code;
    }

    public void setCode(int code)
    {
        this.code = code;
    }

    public String getMsg()
    {
        return msg;
    }

    public void setMsg(String msg)
    {
        this.msg = msg;
    }
}
package com.ruoyi.common.core.page;

import java.io.Serializable;
import java.util.List;

/**
 * 表格分頁數據對象
 * 
 * @author ruoyi
 */
public class TableDataInfo implements Serializable
{
    private static final long serialVersionUID = 1L;

    /** 總記錄數 */
    private long total;

    /** 列表數據 */
    private List<?> rows;

    /** 消息狀態碼 */
    private int code;

    /** 消息內容 */
    private String msg;

    /**
     * 表格數據對象
     */
    public TableDataInfo()
    {
    }

    /**
     * 分頁
     * 
     * @param list 列表數據
     * @param total 總記錄數
     */
    public TableDataInfo(List<?> list, int total)
    {
        this.rows = list;
        this.total = total;
    }

    public long getTotal()
    {
        return total;
    }

    public void setTotal(long total)
    {
        this.total = total;
    }

    public List<?> getRows()
    {
        return rows;
    }

    public void setRows(List<?> rows)
    {
        this.rows = rows;
    }

    public int getCode()
    {
        return code;
    }

    public void setCode(int code)
    {
        this.code = code;
    }

    public String getMsg()
    {
        return msg;
    }

    public void setMsg(String msg)
    {
        this.msg = msg;
    }
}
package com.ruoyi.common.exception.job;

/**
 * 計划策略異常
 * 
 * @author ruoyi
 */
public class TaskException extends Exception
{
    private static final long serialVersionUID = 1L;

    private 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
    }
}

 

package com.ruoyi.common.utils.bean;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Bean 工具類
 * 
 * @author ruoyi
 */
public class BeanUtils extends org.springframework.beans.BeanUtils
{
    /** Bean方法名中屬性名開始的下標 */
    private static final int BEAN_METHOD_PROP_INDEX = 3;

    /** * 匹配getter方法的正則表達式 */
    private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)");

    /** * 匹配setter方法的正則表達式 */
    private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)");

    /**
     * Bean屬性復制工具方法。
     * 
     * @param dest 目標對象
     * @param src 源對象
     */
    public static void copyBeanProp(Object dest, Object src)
    {
        try
        {
            copyProperties(src, dest);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 獲取對象的setter方法。
     * 
     * @param obj 對象
     * @return 對象的setter方法列表
     */
    public static List<Method> getSetterMethods(Object obj)
    {
        // setter方法列表
        List<Method> setterMethods = new ArrayList<Method>();

        // 獲取所有方法
        Method[] methods = obj.getClass().getMethods();

        // 查找setter方法

        for (Method method : methods)
        {
            Matcher m = SET_PATTERN.matcher(method.getName());
            if (m.matches() && (method.getParameterTypes().length == 1))
            {
                setterMethods.add(method);
            }
        }
        // 返回setter方法列表
        return setterMethods;
    }

    /**
     * 獲取對象的getter方法。
     * 
     * @param obj 對象
     * @return 對象的getter方法列表
     */

    public static List<Method> getGetterMethods(Object obj)
    {
        // getter方法列表
        List<Method> getterMethods = new ArrayList<Method>();
        // 獲取所有方法
        Method[] methods = obj.getClass().getMethods();
        // 查找getter方法
        for (Method method : methods)
        {
            Matcher m = GET_PATTERN.matcher(method.getName());
            if (m.matches() && (method.getParameterTypes().length == 0))
            {
                getterMethods.add(method);
            }
        }
        // 返回getter方法列表
        return getterMethods;
    }

    /**
     * 檢查Bean方法名中的屬性名是否相等。<br>
     * 如getName()和setName()屬性名一樣,getName()和setAge()屬性名不一樣。
     * 
     * @param m1 方法名1
     * @param m2 方法名2
     * @return 屬性名一樣返回true,否則返回false
     */

    public static boolean isMethodPropEquals(String m1, String m2)
    {
        return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
    }
}
package com.ruoyi.common.utils.spring;

import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import com.ruoyi.common.utils.StringUtils;

/**
 * spring工具類 方便在非spring管理環境中獲取bean
 * 
 * @author ruoyi
 */
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware 
{
    /** Spring應用上下文環境 */
    private static ConfigurableListableBeanFactory beanFactory;

    private static ApplicationContext applicationContext;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException 
    {
        SpringUtils.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException 
    {
        SpringUtils.applicationContext = applicationContext;
    }

    /**
     * 獲取對象
     *
     * @param name
     * @return Object 一個以所給名字注冊的bean的實例
     * @throws org.springframework.beans.BeansException
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException
    {
        return (T) beanFactory.getBean(name);
    }

    /**
     * 獲取類型為requiredType的對象
     *
     * @param clz
     * @return
     * @throws org.springframework.beans.BeansException
     *
     */
    public static <T> T getBean(Class<T> clz) throws BeansException
    {
        T result = (T) beanFactory.getBean(clz);
        return result;
    }

    /**
     * 如果BeanFactory包含一個與所給名稱匹配的bean定義,則返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name)
    {
        return beanFactory.containsBean(name);
    }

    /**
     * 判斷以給定名字注冊的bean定義是一個singleton還是一個prototype。 如果與給定名字相應的bean定義沒有被找到,將會拋出一個異常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.isSingleton(name);
    }

    /**
     * @param name
     * @return Class 注冊對象的類型
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getType(name);
    }

    /**
     * 如果給定的bean名字在bean定義中有別名,則返回這些別名
     *
     * @param name
     * @return
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getAliases(name);
    }

    /**
     * 獲取aop代理對象
     * 
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker)
    {
        return (T) AopContext.currentProxy();
    }

    /**
     * 獲取當前的環境配置,無配置返回null
     *
     * @return 當前的環境配置
     */
    public static String[] getActiveProfiles()
    {
        return applicationContext.getEnvironment().getActiveProfiles();
    }

    /**
     * 獲取當前的環境配置,當有多個環境配置時,只獲取第一個
     *
     * @return 當前的環境配置
     */
    public static String getActiveProfile()
    {
        final String[] activeProfiles = getActiveProfiles();
        return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
    }
}

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM