虽然目前XXL-JOB这样的分布式任务调度平台比较火,但是如果只是一般的小系统Quart这样的调度框架就可以满足基本需求。
基本思路:通过注解来标记执行定时任务的类(方法),cron表达式设定执行规则,Job的实现类处理具体的逻辑。
用到的依赖
<!--quartz-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
项目结构:
1 TaskJob 实现job接口,重写方法execute(JobExecutionContext context),添加自己的业务逻辑。哪个任务需要调用它,只需要把它的Class对象配置到对应的任务里面;
package com.docker.demo.job.task;
import com.docker.demo.job.annotation.JobMark;
import org.quartz.*;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
@Component
@JobMark
public class TaskJob implements Job {
//立即执行的方法
public static final String METHOD_NAME = "executeNow";
// 定时任务说明
public static final String METHOD_TEXT = "text";
/**
* 执行定时任务
*
* @param context
*/
@Override
public void execute(JobExecutionContext context) {
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
String id = jobDataMap.getString("id");
System.out.println(id + " " + context.getJobDetail().getKey() + ";second:" + LocalDateTime.now().getSecond());
}
/**
* 立即执行
*
* @param id
*/
public void executeNow(Long id) {
System.out.println("立即执行:" + id);
}
public static final String text() {
return "测试任务";
}
}
2 jobServie 写了几个功能,加载所有JobMark注解的类,启动、停止、加载、清空任务的方法。
/*
*/
package com.docker.demo.job.service;
import com.docker.demo.job.vo.ClassNameVO;
import org.quartz.JobKey;
import org.quartz.SchedulerException;
import java.util.List;
import java.util.Set;
/**
* 服务类
*
* @author Chill
*/
public interface IJobService {
/**
* 加载所有的任务类
* @return
*/
List<ClassNameVO> getAllClassNames();
/**
* 启动
* @param taskCode
* @return
*/
boolean start(String taskCode, String className);
/**
* 停止任务
* @param taskCode
* @return
*/
boolean stop(String taskCode);
/**
* 清空所有任务
* @return
* @throws SchedulerException
*/
boolean clear() throws SchedulerException;
boolean fnExecute(String className);
/**
* 初始化加载
*/
Set<JobKey> loadAll();
}
实现类,具体不说了,复制下来应该可以直接用。
package com.docker.demo.job.service.impl;
import com.docker.demo.job.annotation.JobMark;
import com.docker.demo.job.service.IJobService;
import com.docker.demo.job.task.TaskJob;
import com.docker.demo.job.vo.ClassNameVO;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.stereotype.Service;
import java.lang.reflect.Method;
import java.util.*;
/**
* 服务实现类
*
* @author Chill
*/
@Service
public class JobServiceImpl implements IJobService {
@Autowired
private ApplicationContext applicationContext;
// quartz 调度器
@Autowired
private Scheduler scheduler;
/**
* 根据注解JobMark加载自定义定时任务
*
* @return
*/
@Override
public List<ClassNameVO> getAllClassNames() {
String[] strings = applicationContext.getBeanDefinitionNames();
List<ClassNameVO> list = new ArrayList<>();
for (String str : strings) {
if (str.contains(".")) {
continue;
}
Class<?> clazz = getClass(str);
if (clazz.isAnnotationPresent(JobMark.class)) {
String packageName = clazz.getName();
String text = str;
try {
Method textMeth = clazz.getMethod(TaskJob.METHOD_TEXT);
text = textMeth.invoke(null).toString();
} catch (Exception e) {
}
ClassNameVO classNameVO = new ClassNameVO(packageName, text);
list.add(classNameVO);
}
}
Collections.sort(list);
return list;
}
/**
* 根据bean名字加载Class 对象
* @param name
* @return
*/
private Class<?> getClass(String name) {
Object object = applicationContext.getBean(name);
Class<?> clazz = object.getClass();
if (AopUtils.isAopProxy(object)) {
clazz = clazz.getSuperclass();
}
return clazz;
}
@Override
public boolean fnExecute(String className) {
className ="com.docker.demo.job.task.TaskJob";
try {
Class<?> clazz = Class.forName(className);
Method method = clazz.getMethod(TaskJob.METHOD_NAME, Long.class);
method.invoke(applicationContext.getBean(getClassBeanName(className)), 111l);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 根据className 截取bean名字
* @param className
* @return
*/
private static String getClassBeanName(String className) {
className = className.substring(className.lastIndexOf(".") + 1);
className = className.substring(0, 1).toLowerCase() + className.substring(1);
return className;
}
@Override
public boolean start(String taskCode, String className) {
// 测试数据 5秒执行一次
className ="com.docker.demo.job.task.TaskJob";
String cronExpress = "0/5 * * * * ?";
if (cronExpress.indexOf("null") == -1) {
try {
JobKey jobKey = JobKey.jobKey(taskCode,Scheduler.DEFAULT_GROUP);
boolean exist = scheduler.checkExists(jobKey);
System.out.println("exist:"+ exist);
if (exist){
return false;
}
JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
jobDetailFactoryBean.setName(taskCode);
jobDetailFactoryBean.setGroup(Scheduler.DEFAULT_GROUP);
// jobDetailFactoryBean.setJobClass(TaskJob.class);
jobDetailFactoryBean.setJobClass((Class<? extends Job>) Class.forName(className));
//传参
Map map = new HashMap();
map.put("id", "111");
jobDetailFactoryBean.setJobDataMap(getJobDataMap(map));
jobDetailFactoryBean.afterPropertiesSet();
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
cronTriggerFactoryBean.setBeanName(taskCode);
cronTriggerFactoryBean.setCronExpression(cronExpress);
cronTriggerFactoryBean.setGroup(Scheduler.DEFAULT_GROUP);
cronTriggerFactoryBean.setName("cron_" + taskCode);
cronTriggerFactoryBean.afterPropertiesSet();
scheduler.scheduleJob(jobDetailFactoryBean.getObject(), cronTriggerFactoryBean.getObject());
System.out.println(taskCode + "任务启动成功");
return true;
} catch (Exception e) {
e.printStackTrace();
System.out.println(taskCode + "任务启动失败");
}
}
return false;
}
/**
* 将HashMap转为JobDataMap
*
* @param params
* @return
*/
private JobDataMap getJobDataMap(Map<String, String> params) {
JobDataMap jdm = new JobDataMap();
Set<String> keySet = params.keySet();
Iterator<String> it = keySet.iterator();
while (it.hasNext()) {
String key = it.next();
jdm.put(key, params.get(key));
}
return jdm;
}
@Override
public boolean stop(String taskCode) {
try {
scheduler.deleteJob(JobKey.jobKey(taskCode, Scheduler.DEFAULT_GROUP));
System.out.println(taskCode + "任务停止成功");
return true;
} catch (SchedulerException e) {
e.printStackTrace();
System.out.println(taskCode + "任务停止失败");
return false;
}
}
@Override
public boolean clear() throws SchedulerException {
scheduler.clear();
return true;
}
@Override
public Set<JobKey> loadAll() {
Set<JobKey> jobKeySet = new HashSet<>();
try {
jobKeySet = scheduler.getJobKeys(GroupMatcher.anyGroup());
for (JobKey jobKey : jobKeySet) {
System.out.println("jobName:"+jobKey.getName()+";jobGroup:"+jobKey.getGroup());
}
} catch (SchedulerException e) {
e.printStackTrace();
}
return jobKeySet;
}
}
3 自定义注解,和VO
package com.docker.demo.job.annotation;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface JobMark {
}
package com.docker.demo.job.vo;
import lombok.Data;
/**
*
* @author Chill
*/
@Data
public class ClassNameVO implements Comparable<ClassNameVO> {
// 名称
private String name;
//说明
private String text;
public ClassNameVO() {
}
public ClassNameVO(String name, String text) {
this.name = name;
this.text = text;
}
@Override
public int compareTo(ClassNameVO o) {
return this.getName().compareTo(o.getName());
}
}
3 JobController --测试一下
/*
*/
package com.docker.demo.job.Controller;
import com.docker.demo.job.service.IJobService;
import com.docker.demo.job.vo.ClassNameVO;
import lombok.AllArgsConstructor;
import org.quartz.JobKey;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Set;
/**
* 控制器
*
* @author quan
*/
@RestController
@AllArgsConstructor
@RequestMapping("/job")
public class JobController {
@Autowired
private IJobService jobService;
@GetMapping("/getAllClassNames")
@ResponseBody
public List<ClassNameVO> getAllClassNames() {
return jobService.getAllClassNames();
}
@GetMapping("/start")
@ResponseBody
public boolean start() {
return jobService.start("taskCode","");
}
@GetMapping("/fnExecute")
@ResponseBody
public boolean fnExecute() {
return jobService.fnExecute("com.docker.demo.job.task.TaskJob");
}
@GetMapping("/stop")
@ResponseBody
public boolean stop() {
return jobService.stop("taskCode");
}
@GetMapping("/clear")
@ResponseBody
public boolean clear() throws SchedulerException {
return jobService.clear();
}
@GetMapping("/loadAll")
@ResponseBody
public Set<JobKey> loadAll() {
return jobService.loadAll();
}
}
3.1 加载所有的任务类
3.2 启动查看控制台
3.3 加载正在运行的任务
这样,基本的功能就算实现了。更复杂的业务需要根据具体情况去处理了。