spring4和quartz2.x整合,對任務做動態操作(增刪改查)

quartz 1.x版本和quartz2.x版本變化有點大,所以開始使用quartz時要注意版本

項目結構如下


本demo 的設計思想是對定時任務的增刪改查等,需要通過相關的反射知識,及通過任務描述類中group和trigger來得到相關的job

項目使用maven jar如下

        

                <dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.3.0</version>
		</dependency>
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz-jobs</artifactId>
			<version>2.3.0</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>4.1.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.1.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version> 4.1.2.RELEASE  </version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.1.2.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>4.1.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.39</version>
		</dependency>

寫一個操作定時任務的service 接口

package com.quartz.service;

import java.util.List;

import org.quartz.Trigger;

import com.quartz.ScheduleTask;

public interface TaskService {
	/**
	 * 獲取所有的定時任務
	 * @return
	 */
	public List<ScheduleTask> getAllTask();
	/**
	 * 獲取觸發器
	 * @param trigger 觸發器名稱(這裏相當於方法名)
	 * @param group 任務組名稱(這裏相當於job所在的類名稱)
	 * @return
	 */
	public Trigger getTrigger(String trigger,String group);
	/**
	 * 添加一個新的定時任務
	 * @param task
	 * @return
	 */
	public ScheduleTask addTask(ScheduleTask task);
	/**
	 * 重啓定時任務
	 * @param task
	 * @return
	 */
	public ScheduleTask reStartTask(ScheduleTask task);
	/**
	 * 刪除指定定時任務
	 * @param task
	 * @return
	 */
	public ScheduleTask deleteTask(ScheduleTask task);
	/**
	 * 暫停任務
	 * @param task
	 * @return
	 */
	public ScheduleTask pauseTask(ScheduleTask task);
	/**
	 * 恢復任務
	 * @param task
	 * @return
	 */
	public ScheduleTask resumeTask(ScheduleTask task);
	/**
	 * 批量刪除定時任務
	 */
	public void deleteTasks(List<ScheduleTask> scheduleTasks);
	/**
	 * 暫停所有的定時任務
	 */
	public void pauseAllTask();
	
}
實現類如下:

package com.quartz.service.impl;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;

import com.quartz.ScheduleTask;
import com.quartz.job.MyJob2;
import com.quartz.service.TaskService;

@Service("taskService")
public class TaskServiceImpl implements TaskService {

	private ConcurrentHashMap<String, ScheduleTask> allTask = new ConcurrentHashMap<String, ScheduleTask>();

	@Autowired
	private SchedulerFactoryBean schedulerFactoryBean;

	public List<ScheduleTask> getAllTask() {
		List<ScheduleTask> list = new ArrayList();
		list.addAll(allTask.values());
		return list;
	}

	public CronTrigger getTrigger(String trigger, String group) {
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		CronTrigger cronTrigger = null;
		try {
			cronTrigger = (CronTrigger) scheduler.getTrigger(new TriggerKey(
					trigger, group));
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return cronTrigger;
	}

	public ScheduleTask addTask(ScheduleTask task) {
		try {
			Scheduler scheduler = schedulerFactoryBean.getScheduler();
			JobDetail jobDetail = JobBuilder.newJob(MyJob2.class)
					.withIdentity(task.getName(), task.getGroup()).build();
			Trigger trigger = TriggerBuilder
					.newTrigger()
					.withIdentity(task.getTrigger(), task.getGroup())
					.startNow()
					.withSchedule(
							CronScheduleBuilder.cronSchedule(task
									.getExpression())).build();
			System.out.println(trigger.getKey());
			Class<?> classzz = Class.forName(task.getGroup());
			Class<?>[] c = new Class<?>[task.getParam().length];
			for (int i = 0; i < task.getParam().length; i++) {
				c[i] = task.getParam()[i].getClass();
			}
			Method method = classzz.getMethod(task.getTrigger(), c);
			JobDataMap jobDataMap = trigger.getJobDataMap();
			jobDataMap.put(MyJob2.JOB_NAME, task.getName());
			jobDataMap.put(MyJob2.JOB_GROUP, classzz);
			jobDataMap.put(MyJob2.JOB_TRIGGER, method);
			jobDataMap.put(MyJob2.JOB_TRIGGER_PARAM, task.getParam());
			scheduler.scheduleJob(jobDetail, trigger);
			if (scheduler.isStarted()) {
				scheduler.start();
			}
			if (!allTask.containsKey(task.getId())) {
				allTask.put(task.getId(), task);
			}
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return task;
	}

	public ScheduleTask reStartTask(ScheduleTask task) {
		try {
			CronTrigger cronTrigger = getTrigger(task.getTrigger(),
					task.getGroup());
			Scheduler scheduler = schedulerFactoryBean.getScheduler();
			cronTrigger = cronTrigger
					.getTriggerBuilder()
					.withIdentity(
							new TriggerKey(task.getTrigger(), task.getGroup()))
					.withSchedule(
							CronScheduleBuilder.cronSchedule(task
									.getExpression())).build();

			// 按新的trigger重新設置job執行

			scheduler.rescheduleJob(
					new TriggerKey(task.getTrigger(), task.getGroup()),
					cronTrigger);
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// 下方可以做一些更新數據庫中任務的操作

		return task;
	}

	public ScheduleTask deleteTask(ScheduleTask task) {
		try {
			Scheduler scheduler = schedulerFactoryBean.getScheduler();
			JobKey jobKey = new JobKey(task.getName(), task.getGroup());
			scheduler.deleteJob(jobKey);
		} catch (SchedulerException e) {

			e.printStackTrace();
		}
		return task;
	}

	public ScheduleTask pauseTask(ScheduleTask task) {
		try {
			Scheduler scheduler = schedulerFactoryBean.getScheduler();
			JobKey jobKey = new JobKey(task.getName(), task.getGroup());
			scheduler.pauseJob(jobKey);
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return task;
	}

	public ScheduleTask resumeTask(ScheduleTask task) {
		try {
			Scheduler scheduler = schedulerFactoryBean.getScheduler();
			JobKey jobKey = new JobKey(task.getName(), task.getGroup());
			scheduler.resumeJob(jobKey);
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return task;
	}

	public void deleteTasks(List<ScheduleTask> scheduleTasks) {
		try {
			Scheduler scheduler = schedulerFactoryBean.getScheduler();
			List<JobKey> jobKeys = new ArrayList<JobKey>();
			JobKey jobKey;
			for (ScheduleTask scheduleTask : scheduleTasks) {
				jobKey = new JobKey(scheduleTask.getName(),
						scheduleTask.getGroup());
				jobKeys.add(jobKey);
			}
			scheduler.deleteJobs(jobKeys);
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void pauseAllTask() {
		try {
			Scheduler scheduler = schedulerFactoryBean.getScheduler();
			scheduler.pauseAll();
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
本項目中打算將所有的定時任務都管理起來,包括@Scheduled註解的任務,需要將配置文件中<task:annotation-driven/>去掉,我們手動的啓動註解任務

ManagerJob.java 如下

package com.quartz.job;

import java.lang.reflect.Method;

import javax.annotation.Resource;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.quartz.ScheduleTask;
import com.quartz.service.TaskService;
import com.quartz.util.SnowflakeIdWorker;
@Component
public class ManagerJob implements ApplicationListener<ContextRefreshedEvent> {

    @Resource
	private TaskService taskService;
	
	public void onApplicationEvent(ContextRefreshedEvent arg0) {
		ApplicationContext applicationContext = arg0.getApplicationContext();
		String[] beans=applicationContext.getBeanDefinitionNames();
		for(String bean :beans){
			Class<?> class1 = applicationContext.getBean(bean).getClass();
			Method[] methods = class1.getMethods();
			for(Method method:methods){
				if(method.isAnnotationPresent(Scheduled.class)){
					Scheduled annotation = method.getAnnotation(Scheduled.class);
					ScheduleTask scheduleTask=new ScheduleTask();
					scheduleTask.setGroup(class1.getName());
					scheduleTask.setTrigger(method.getName());
					scheduleTask.setId(String.valueOf(SnowflakeIdWorker.getInstance().nextId()));
					scheduleTask.setExpression(annotation.cron());
					scheduleTask.setParam(new Object[]{});
					scheduleTask.setName("task2");
					taskService.addTask(scheduleTask);
				}
			}
		}
	}

}
自定義MyJob,不實現Job接口的

package com.quartz.job;

public class MyJob {

	public void execute() {
		System.out.println("---------執行----MyJob-----");
	}
	
	public void findUserName(String name,Integer age){
		System.out.println("歡迎"+age+"歲的"+name+"光臨");
	}

}
代理MyJob2 實現Job接口的

package com.quartz.job;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class MyJob2 implements Job {

	public static final String JOB_NAME="job_name";
	public static final String JOB_GROUP="job_group";
	public static final String JOB_TRIGGER="job_trigger";
	public static final String JOB_TRIGGER_PARAM="job_trigger_param";
	public void execute(JobExecutionContext context)
			throws JobExecutionException {
		 try {
		 System.out.println("----執行------"+new SimpleDateFormat("yyyy-MM-dd HH:mm:dd:ss").format(new Date()));
         JobDataMap jobDataMap = context.getTrigger().getJobDataMap();
 		 String jobName=(String) jobDataMap.get(MyJob2.JOB_NAME);
 		 Class<?> classzz=(Class<?>) jobDataMap.get(MyJob2.JOB_GROUP);
 		 Method method=(Method) jobDataMap.get(MyJob2.JOB_TRIGGER);
 		 Object[] objects=(Object[]) jobDataMap.get(MyJob2.JOB_TRIGGER_PARAM);
		 method.invoke(classzz.newInstance(), objects);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
MyJob3 通過註解實現的定時任務

package com.quartz.job;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class MyJob3 {

	@Scheduled(cron="0/5 * * * * ?")
	public void execute(){
		System.out.println("------執行註解實現的定時任務-----");
	}
}

定時任務的描述類ScheduleTask

package com.quartz;
import java.io.Serializable;
public class ScheduleTask implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -6696079243640708701L;
	
	private String id;//任務Id
	private String name;//任務名
	private String group;//任務所在類全名稱
	private String trigger;//任務執行的方法名
	private String expression;//任務頻率 和cron語法保持一致
	private Object[] param;//執行任務方法的參數
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getGroup() {
		return group;
	}
	public void setGroup(String group) {
		this.group = group;
	}
	public String getTrigger() {
		return trigger;
	}
	public void setTrigger(String trigger) {
		this.trigger = trigger;
	}
	public String getExpression() {
		return expression;
	}
	public void setExpression(String expression) {
		this.expression = expression;
	}
	public Object[] getParam() {
		return param;
	}
	public void setParam(Object[] param) {
		this.param = param;
	}
}

spring-quartz2配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:task="http://www.springframework.org/schema/task"  
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
						http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/aop 
						http://www.springframework.org/schema/aop/spring-aop.xsd
						http://www.springframework.org/schema/context 
						http://www.springframework.org/schema/context/spring-context.xsd
						http://www.springframework.org/schema/tx 
						http://www.springframework.org/schema/tx/spring-tx.xsd
					    http://www.springframework.org/schema/util
					    http://www.springframework.org/schema/util/spring-util.xsd
					        http://www.springframework.org/schema/task  
    http://www.springframework.org/schema/task/spring-task-3.1.xsd">
	
	<context:component-scan base-package="com.quartz"></context:component-scan>
	<!-- <task:annotation-driven/> -->
	<!-- ======================== 調度工廠 ======================== -->
	<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
	</bean>
</beans>

測試類

package com.quartz.test;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.quartz.ScheduleTask;
import com.quartz.service.TaskService;

@ContextConfiguration(locations={"classpath:spring-quartz2.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class QuartzTest {

	@Autowired
	private TaskService taskService;
	
	@Test
	public void addTask() throws InterruptedException{
		System.out.println("test--------start----------");
		ScheduleTask task=new ScheduleTask();
		task.setExpression("0/5 * * * * ?");
		task.setId("001");
		task.setGroup("com.quartz.job.MyJob");
		task.setTrigger("findUserName");
		task.setName("task1");
		task.setParam(new Object[]{"zyc",22});
		List<ScheduleTask> list=taskService.getAllTask();
		System.out.println(JSON.toJSONString(list));
//		taskService.addTask(task);
//		Thread.sleep(1000*20);
//		System.out.println("重啓定時任務");
//		task.setExpression("0/10 * * * * ?");
//		taskService.reStartTask(task);
		while(true){}
	}
}

完成以上還需要添加log4j.properties 配置文件,測試使用

可使用如下,或者網上自己搜

log4j.rootLogger=DEBUG,Console,File

log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target=System.out
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=[%p][%d{HH\:mm\:ss,SSS}][%c]%m%n

log4j.appender.File=org.apache.log4j.RollingFileAppender 
log4j.appender.File.File=quart.log
log4j.appender.File.MaxFileSize=10MB
log4j.appender.File.Threshold=ALL
log4j.appender.File.layout=org.apache.log4j.PatternLayout
log4j.appender.File.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH\:mm\:ss,SSS}][%c]%m%n

log4j.logger.org.quartz=WARN,Console,File
log4j.logger.org.activiti=WARN,Console,File
log4j.logger.org.apache=WARN,Console,File
log4j.logger.org.mybatis=WARN,Console,File
log4j.logger.net.sf.ehcache=WARN,Console,File
log4j.logger.org.springframework=WARN,Console,File
log4j.logger.org.springframework.jdbc.core.JdbcTemplate=debug,Console,File
log4j.logger.freemarker=WARN,Console,File

SnowflakeIdWorker 是id生成器
id生成器使用的是雪花算法如下 可百度分佈式id生成器

package com.quartz.util;
public class SnowflakeIdWorker {

	private static SnowflakeIdWorker SnowflakeIdWorker=new SnowflakeIdWorker();
	//聲明私有構造函數
	private SnowflakeIdWorker(){
		
	}
    // ==============================Fields===========================================
    /** 開始時間截 (2015-01-01) */
    private final long twepoch = 1420041600000L;

    /** 機器id所佔的位數 */
    private final long workerIdBits = 5L;

    /** 數據標識id所佔的位數 */
    private final long datacenterIdBits = 5L;

    /** 支持的最大機器id,結果是31 (這個移位算法可以很快的計算出幾位二進制數所能表示的最大十進制數) */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /** 支持的最大數據標識id,結果是31 */
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    /** 序列在id中佔的位數 */
    private final long sequenceBits = 12L;

    /** 機器ID向左移12位 */
    private final long workerIdShift = sequenceBits;

    /** 數據標識id向左移17位(12+5) */
    private final long datacenterIdShift = sequenceBits + workerIdBits;

    /** 時間截向左移22位(5+5+12) */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    /** 生成序列的掩碼,這裏爲4095 (0b111111111111=0xfff=4095) */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /** 工作機器ID(0~31) */
    private long workerId;

    /** 數據中心ID(0~31) */
    private long datacenterId;

    /** 毫秒內序列(0~4095) */
    private long sequence = 0L;

    /** 上次生成ID的時間截 */
    private long lastTimestamp = -1L;

    //==============================Constructors=====================================
    /**
     * 構造函數
     * @param workerId 工作ID (0~31)
     * @param datacenterId 數據中心ID (0~31)
     */
    public SnowflakeIdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    // ==============================Methods==========================================
    /**
     * 獲得下一個ID (該方法是線程安全的)
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        //如果當前時間小於上一次ID生成的時間戳,說明系統時鐘回退過這個時候應當拋出異常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        //如果是同一時間生成的,則進行毫秒內序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒內序列溢出
            if (sequence == 0) {
                //阻塞到下一個毫秒,獲得新的時間戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //時間戳改變,毫秒內序列重置
        else {
            sequence = 0L;
        }

        //上次生成ID的時間截
        lastTimestamp = timestamp;

        //移位並通過或運算拼到一起組成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift) //
                | (datacenterId << datacenterIdShift) //
                | (workerId << workerIdShift) //
                | sequence;
    }

    /**
     * 阻塞到下一個毫秒,直到獲得新的時間戳
     * @param lastTimestamp 上次生成ID的時間截
     * @return 當前時間戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒爲單位的當前時間
     * @return 當前時間(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    //==============================Test=============================================
    /** 測試 */
    public static void main(String[] args) {
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
        for (int i = 0; i < 1000; i++) {
            long id = idWorker.nextId();
            System.out.println(Long.toBinaryString(id));
            System.out.println(id);
        }
    }
    
    public static SnowflakeIdWorker getInstance(){
    	return SnowflakeIdWorker;
    }
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章