SpringBoot+Quartz基本用例-启动-停止-查询-清空

虽然目前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 加载正在运行的任务

这样,基本的功能就算实现了。更复杂的业务需要根据具体情况去处理了。

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值