最近在项目中要对数据进行统计,而且每个商户的统计时间不一定项目,如果要根据之前的xml格式去配置的话,那么200多个商户配置要相当大的工作量,而且不一定商户的统计时间是固定的,比如一个商家的日数据比较少,统计一段时间之后,商家觉得日统计数据不够直白,想要加入周统计或者月统计功能,这时候如果再次添加xml文件,工作量又会很大而且不容易维护,还要对项目进行关闭和重启,体验感就相当差了。
那么我们就结合spring 和quartz 来实现动态的定时器,spring3.1以下的版本必须使用quartz1.x系列,3.1以上的版本才支持quartz 2.x,不然会出错。 原因:spring对于quartz的支持实现,org.springframework.scheduling.quartz.CronTriggerBean继承了org.quartz.CronTrigger,在quartz1.x系列中org.quartz.CronTrigger是个类,而在quartz2.x系列中org.quartz.CronTrigger变成了接口,从而造成无法用spring的方式配置quartz的触发器(trigger)
首先我们需要把架包引入项目中,推荐使用maven导入,至于spring的架包就不在这里引入了,我想大家都知道吧:
<!-- Slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.8</version>
</dependency>
<!-- quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>1.8.5</version>
</dependency>
架
包有了,那么我们就开始使用起来吧,先定义一个schedule的辅助类,用于添加或者删除定时器功能
/**
* 计划任务信息 (定时器任务)
* @author lwlong
* @create 2017-8-9 上午10:18:24
* @version 1.0
*/
public class ScheduleJob implements Serializable {
private static final long serialVersionUID = 1L;
//任务ID
private String jobId;
//任务名称
private String jobName;
//任务 分组
private String jobGroup;
//任务状态 0禁用 1启用 2删除
private String jobStatus;
//任务运行时间表达式
private String cronExpression;
//任务描述
private String desc;
//触发器名
private String triggerName;
//触发器组
private String triggerGroupName;
public ScheduleJob(){}
public ScheduleJob(String jobName,String triggerName,String cronExpression,String jobGroup,String triggerGroupName){
this.jobName = jobName;
this.triggerName = triggerName;
this.cronExpression = cronExpression;
this.jobGroup = jobGroup;
this.triggerGroupName = triggerGroupName;
}
public String getTriggerName() {
return triggerName;
}
public void setTriggerName(String triggerName) {
this.triggerName = triggerName;
}
public String getTriggerGroupName() {
return triggerGroupName;
}
public void setTriggerGroupName(String triggerGroupName) {
this.triggerGroupName = triggerGroupName;
}
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
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 getJobStatus() {
return jobStatus;
}
public void setJobStatus(String jobStatus) {
this.jobStatus = jobStatus;
}
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
辅助类也有了,我个人比较喜欢写工具类,这样在每一个项目中都可以迅速的使用起来,而且耦合性也很低,不影响整体的框架使用。那么我们来看下我的工具类把
import java.util.HashMap;
import java.util.Map;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
import com.lwl.entity.RemindInfo;
import com.lwl.entity.schedule.ScheduleJob;
import com.lwl.quartz.QuartzRemindWorkJob;
public class QuartzManagerUtil {
private static SchedulerFactory gSchedulerFactory = new StdSchedulerFactory();
//任务对于的map
private static Map<String, ScheduleJob> jobMap = new HashMap<String, ScheduleJob>();
/**
* @Description: 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
*
* @param jobName
* 任务名
* @param cls
* 任务
* @param time
* 时间设置,参考quartz说明文档
*
* @Title: QuartzManager.java
*/
@SuppressWarnings("rawtypes")
private static void addJob(ScheduleJob job, Class cls) {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
JobDetail jobDetail = new JobDetail(job.getJobName(), job.getJobGroup(), cls);// 任务名,任务组,任务执行类
// 触发器
CronTrigger trigger = new CronTrigger(job.getJobName(), job.getTriggerGroupName());// 触发器名,触发器组
trigger.setCronExpression(job.getCronExpression());// 触发器时间设定
sched.scheduleJob(jobDetail, trigger);
// 启动
if (!sched.isShutdown()) {
sched.start();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 移除一个任务(使用默认的任务组名,触发器名,触发器组名)
*
* @param jobName
*/
private static void removeJob(ScheduleJob job) {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.pauseTrigger(job.getJobName(), job.getTriggerGroupName());// 停止触发器
sched.unscheduleJob(job.getJobName(), job.getTriggerGroupName());// 移除触发器
sched.deleteJob(job.getJobName(), job.getJobGroup());// 删除任务
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 移除定时任务 通过ID 和openId 确保唯一
* @param info
* @author liuweilong@zhicall.com
* @create 2016-5-13 下午4:02:26
*/
public static void removeQuartz(RemindInfo info){
ScheduleJob job = jobMap.get(info.getId()+"_"+info.getOpenId());
if(job!=null){
removeJob(job);
}
}
/**
* 添加定时任务
* @param info
* @author liuweilong@zhicall.com
* @create 2016-5-13 下午4:03:15
*/
public static void addQuartz(RemindInfo info){
String key = info.getId()+"_"+info.getOpenId();
if(jobMap.get(key)!=null){
return;
}
addJob(createScheduleJob(info),QuartzRemindWorkJob.class);
}
/**
*创建定时任务属性
* @param info
* @return
* @author liuweilong@zhicall.com
* @create 2016-5-13 下午4:03:35
*/
private static ScheduleJob createScheduleJob(RemindInfo info){
String cycle = "day";
String jobName =info.getId()+"_"+info.getOpenId();
String jobGroup =info.getOpenId()+"_"+cycle+"_"+info.getId()+"_group";
String triggerName =info.getOpenId()+"_"+cycle+"_"+info.getId()+"_trigger";
String triggerGroupName =info.getOpenId()+"_"+cycle+"_"+info.getId()+"_trigger_group";
ScheduleJob job = new ScheduleJob(jobName, triggerName, info.getCronExpression(), jobGroup, triggerGroupName);
jobMap.put(info.getId()+"_"+info.getOpenId(), job);
return job;
}
}
你会发现这个工具类中只有2个方法的对外使用的,一个新增的方法addQuartz(RemindInfo info),而另一个则是删除的方法removeQuartz(RemindInfo info),而这个参数就是你们将要统计对象的实体类,
这样做的方式就是可以把要统计的对象和定时器关联起来,
你们可以看到定时器的jobName的命名方式是通过统计实体类的id_+统计参数的特殊属性形成,
这里同时用一个map记录所有的定时器的jobName,为了是方便删除定时器。
其中 private static ScheduleJob createScheduleJob(RemindInfo info)这个方法特别的重要,
其中jobName是关联统计类的,而info.getCronExpression()这个值是特别的重要,这个值就是让你统计触发的时间点的表达式:
来我们看看现在是怎么使用的呢?
/**
* 发送提醒的定时器
* @author lwlong
* @create 2017-8-9 上午10:21:23
* @version 1.0
*/
public class QuartzRemindWorkJob implements Job {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
// 获取 Spring 配置的 Service//这里加载的时候有记载了一个定时器!!!!
public static final ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
IOpenIdFormIdDao openIdFormIdDao = (IOpenIdFormIdDao) BeanFactory.getInstance().getBean("openIdFormIdDao");
//获取定时任务名称
String Jobname = context.getJobDetail().getName();
String[] array = Jobname.split("_");
String openId = array[1];
logger.info("QuartzRemindWorkJob:execute 开始执行定时推送:定时器名称为:"+Jobname);
.....
......
logger.info("QuartzRemindWorkJob:execute 结束执行定时推送:定时器名称为:"+Jobname);
}
}
好了,全部代码已经完成了,大功告成,还不赶紧试试看。