<!-- 定时任务 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<exclusions>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</exclusion>
</exclusions>
</dependency>
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) DEFAULT NULL COMMENT '调用目标 列:类名.方法名',
`cron_expression` varchar(255) DEFAULT '' COMMENT 'cron执行表达式',
`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 '备注信息',
`isdel` int(2) DEFAULT '0' COMMENT '是否删除 0未删除,1已删除',
PRIMARY KEY (`job_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='定时任务调度表';
QuartzConfig:
在Configuration中配置Scheduler实例,并启动。
/**
* 定时任务配置(单机部署建议删除此类和qrtz数据库表,默认走内存会最高效)
*
* @author huang
*/
@Configuration
public class QuartzConfig {
@Bean
public Scheduler scheduler(){
Scheduler scheduler = null;
SchedulerFactory factory = new StdSchedulerFactory();
try {
scheduler = factory.getScheduler();
} catch (SchedulerException e) {
e.printStackTrace();
}
if(scheduler != null){
try {
//启动定时任务
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
return scheduler;
}
}
JobInvokeUtil:
/**
* 任务执行工具
* @author huangjj
*/
public class JobInvokeUtil
{
public static String TASK_PROPERTIES = "TASK_PROPERTIES";
/**
* 执行方法
*
// * @param sysJob 系统任务
*/
public static Object invokeMethod(SysJob sysJob) throws Exception
{
String invokeTarget = sysJob.getInvokeTarget();
String beanName = getBeanName(invokeTarget);
String methodName = getMethodName(invokeTarget);
List<Object[]> methodParams = getMethodParams(invokeTarget);
Object bean = null;
if (!isValidClassName(beanName))
{
bean = SpringUtils.getBean(beanName);
invokeMethod(bean, methodName, methodParams);
}
else
{
bean = Class.forName(beanName).newInstance();
invokeMethod(bean, methodName, methodParams);
}
return bean;
}
/**
* 调用任务方法
*
* @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 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] = (Object) os[0];
index++;
}
return classs;
}
}
QuartzJob:
任务类JOB就是定时任务具体要处理的系统业务逻辑,需要实现Job接口。在任务启动时,通过jobClass传入JobDetail。
/**
* 抽象quartz调用
*
* @author huang
*/
public abstract class QuartzJob implements Job {
private static final Logger log = LoggerFactory.getLogger(QuartzJob.class);
@Override
public void execute(JobExecutionContext context) {
try{
SysJob sysJob = new SysJob();
BeanUtils.copyProperties(context.getMergedJobDataMap().get(JobInvokeUtil.TASK_PROPERTIES),sysJob);
doExecute(context,sysJob);
System.out.println("执行定时任务:"+new Date());
}catch (Exception e){
log.error("任务执行异常 - :", e);
e.printStackTrace();
}
}
/**
* 执行方法,由子类重载
*
* @param context 工作执行上下文对象
* @param sysJob 系统计划任务
* @throws Exception 执行过程中的异常
*/
protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
}
QuartzJobExecution:
/**
* 定时任务处理(允许并发执行)
*
* @author ruoyi
*
*/
public class QuartzJobExecution extends QuartzJob
{
@Override
protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception
{
JobInvokeUtil.invokeMethod(sysJob);
}
}
QuartzService :
/**
* 定时任务管理服务
*/
@Service
public class QuartzService {
@Autowired
private Scheduler scheduler;
/**
* 启动任务
* @param jobId 任务ID
* @param jobGroup 任务名称
* @param cron 执行正则
* @throws Exception
*/
public void startJob(String jobId, String cron, String jobGroup) throws Exception{
Class<? extends Job> jobClass = QuartzJobExecution.class;//获取任务执行类
//创建job,指定job名称和分组
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobId, jobGroup).build();
//创建表达式工作计划
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
//创建触发器
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobId, jobGroup)
.withSchedule(cronScheduleBuilder).build();
// TODO jobId:sys_job的ID,sysJobMapper数据层查询任务数据
SysJob job = sysJobMapper.selectById(jobId);
// 放入参数,运行时的方法可以获取
jobDetail.getJobDataMap().put(JobInvokeUtil.TASK_PROPERTIES, job);
scheduler.scheduleJob(jobDetail, cronTrigger);
}
/**
* 修改定时任务执行时间
* @param jobId 任务ID
* @param jobGroup 任务名称
* @param cron 执行正则
* @throws Exception
*/
public void modifyJob(String jobId, String jobGroup, String cron) throws Exception{
TriggerKey triggerKey = new TriggerKey(jobId, jobGroup);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
String oldCron = trigger.getCronExpression();
if(!oldCron.equals(cron)){
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobId, jobGroup)
.withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
Date date = scheduler.rescheduleJob(triggerKey, cronTrigger);
if(date == null){
throw new Exception("修改定时任务执行时间报错");
}
}
}
/**
* 暂停某个定时任务(任务恢复后,暂停时间段内未执行的任务会继续执行,如暂停时间段内有2次,则会执行2次)
* @param jobId 任务ID
* @param jobGroup 任务名称
* @throws Exception
*/
public void pauseJob(String jobId, String jobGroup) throws Exception{
JobKey jobKey = new JobKey(jobId, jobGroup);
scheduler.pauseJob(jobKey);
}
/**
* 恢复某个定时任务
* @param jobId 任务ID
* @param jobGroup 任务名称
* @throws Exception
*/
public void resumeJob(String jobId, String jobGroup) throws Exception{
JobKey jobKey = new JobKey(jobId, jobGroup);
scheduler.resumeJob(jobKey);
}
/**
* 删除某个定时任务
* @param jobId 任务ID
* @param jobGroup 任务名称
* @throws Exception
*/
public void deleteJob(String jobId, String jobGroup) throws Exception{
JobKey jobKey = new JobKey(jobId, jobGroup);
scheduler.deleteJob(jobKey);
}
}
TestTask:
/**
* 定时任务调度测试
*
* @author huang
*/
@Component("task")
public class Task
{
public void multipleParams(String s, Boolean b, Long l, Double d, Integer i)
{
System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i));
}
public void params(String params)
{
System.out.println("执行有参方法:" + params);
}
public void noParams()
{
System.out.println("执行无参方法noParams"+new Date());
}
public void noParamsOne()
{
System.out.println("执行无参方法noParamsOne"+new Date());
}
}
通过Controller提供API接口,这里我的TaskService调用了QartzService的对应接口,并做了一个写数据库读写操作,主要记录定时任务状态、执行记录信息的等。
@RestController
@RequestMapping("/quartz")
public class QuartzController {
@Autowired
private QuartzService service;
@RequestMapping("/start")
public Object start(String id,String cron,String jobGroup){
try {
service.startJob(id,cron,jobGroup);
return "ok";
} catch (Exception e) {
return "error";
}
}
@RequestMapping("/pause")
public Object pause(String id, String jobGroup){
try {
service.pauseJob(id,jobGroup);
return "ok";
} catch (Exception e) {
return "error";
}
}
@RequestMapping("/resume")
public Object resume(String id, String jobGroup){
try {
service.resumeJob(id,jobGroup);
return "ok";
} catch (Exception e) {
return "error";
}
}
@RequestMapping("/remove")
public Object remove(String id, String jobGroup){
try {
service.deleteJob(id,jobGroup);
return "ok";
} catch (Exception e) {
return "error";
}
}
}