文章目录
一.前言
最近在学习定时任务框架Quartz,怎么说呢,就是感觉Quartz提供的Api使用起来并不是那么方便!一个定时任务的执行至少需要三个组件Job、Trigger、Scheduler!用得不太开心,于是决定将一些简单常用的任务模式封装一下。于是这篇博客诞生了!
二.工具简介
这个小工具封装了以下三种模式的任务
1.Cron表达式模式 --> 按照cron表达式的规则执行任务
2.固定延时模式 --> 等待一个固定时间后再执行该任务
3.循环模式 --> 按照某个固定的时间间隔重复执行一个任务(重复次数可控)
三.下载地址
下载地址:网盘地址
提 取 码:kk2n
四.工具的使用
4.1 定义定时任务组件
定义任务组件十分简单,编写一个普通的类就好了,不用实现接口不用继承其他父类也不用任何注解!
1.任务组件:MyJob.java
import quartz.ann.Job;
public class MyJob {
public void showTime(String k){
System.out.println(DateUtils.time()+" ==> showTime("+k+") [每分钟的第30秒开始执行,每3秒执行一次] Running......");
}
public void time(){
System.out.println(DateUtils.time()+" ==> time() [调用时立即执行,重复执行4次后结束,执行的时间间隔为2秒] Running......");
}
public void fixed(){
System.out.println(DateUtils.time()+" ==> fixed() [延时7秒执行] Running......");
}
public void ttt(){
System.out.println("kokokok");
}
}
4.2 使用@Job注解定义触发规则
2.使用@Job定义触发规则:MyJob.java
@Job注解属性描述
cron :使用Cron表达式来描述触发规则
fixedDelay:[固定延时触发]在主动调用该注解标注的方法时,会先延迟fixedDela毫秒后执行一次后结束。单位:msperformInterval + performCount:[循环触发]
performInterval:任务执行的间隔时间 默认:1秒
performCount:循环执行的次数,值小于1时表示无限循环的执行 默认:-1
import quartz.ann.Job;
public class MyJob {
//每分钟的第30秒开始执行,每3秒执行一次
@Job(cron = "30/3 * * * * ?")
public void showTime(String k){
System.out.println(DateUtils.time()+" ==> showTime("+k+") [每分钟的第30秒开始执行,每3秒执行一次] Running......");
}
//调用时立即执行,重复执行4次后结束,执行的时间间隔为2秒
@Job(performInterval = 2*1000L,performCount = 4)
public void time(){
System.out.println(DateUtils.time()+" ==> time() [调用时立即执行,重复执行4次后结束,执行的时间间隔为2秒] Running......");
}
//调用后延时7秒执行
@Job(fixedDelay = 7*1000L)
public void fixed(){
System.out.println(DateUtils.time()+" ==> fixed() [延时7秒执行] Running......");
}
public void ttt(){
System.out.println("kokokok");
}
}
4.3 得到组件代理并执行任务
使用QuartzProxy的getProxy方法工具类得到一个任务组件的代理对象
import org.quartz.SchedulerException;
import quartz.cglib.QuartzProxy;
public class JobTestMain {
public static void main(String[] args) throws SchedulerException {
//得到任务组件的代理对象
MyJob job = QuartzProxy.getProxy(MyJob.class);
//执行定时任务
job.time();
job.showTime("FK");
job.showTime("JACK");
job.ttt();
job.fixed();
}
}
控制台:
kokokok
2020-07-25 21:41:27 ==> time() [调用时立即执行,重复执行4次后结束,执行的时间间隔为2秒] Running......
2020-07-25 21:41:29 ==> time() [调用时立即执行,重复执行4次后结束,执行的时间间隔为2秒] Running......
2020-07-25 21:41:30 ==> showTime(JACK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:30 ==> showTime(FK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:31 ==> time() [调用时立即执行,重复执行4次后结束,执行的时间间隔为2秒] Running......
2020-07-25 21:41:33 ==> showTime(JACK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:33 ==> showTime(FK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:33 ==> time() [调用时立即执行,重复执行4次后结束,执行的时间间隔为2秒] Running......
2020-07-25 21:41:34 ==> fixed() [延时7秒执行] Running......
2020-07-25 21:41:36 ==> showTime(JACK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:36 ==> showTime(FK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:39 ==> showTime(JACK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:39 ==> showTime(FK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:42 ==> showTime(JACK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:42 ==> showTime(FK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:45 ==> showTime(JACK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:45 ==> showTime(FK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:48 ==> showTime(JACK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:48 ==> showTime(FK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:51 ==> showTime(JACK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:51 ==> showTime(FK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:54 ==> showTime(JACK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
2020-07-25 21:41:54 ==> showTime(FK) [每分钟的第30秒开始执行,每3秒执行一次] Running......
五.工具源码分析
5.1 Cglib代理器
CglibProxg.java
package quartz.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
/**
* Cglib动态代理
*/
public abstract class CglibProxy {
public static <T> T getCglibProxyObject(Class<T> clazz, MethodInterceptor methodInterceptor){
final Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(methodInterceptor);
return (T) enhancer.create();
}
}
5.2 代理逻辑封装器
QuartzMethodInterceptor.java
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import quartz.TargetJobRun;
import quartz.ann.Job;
import quartz.job.LuckyJob;
import java.lang.reflect.Method;
import java.util.UUID;
import static org.quartz.DateBuilder.futureDate;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static quartz.constant.Constant.LUCKY_JOB;
import static quartz.constant.Constant.LUCKY_JOB_GROUP;
public class QuartzMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object targetObj, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
if(method.isAnnotationPresent(Job.class)){
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
String jtname= UUID.randomUUID().toString();
Job quartzJob = method.getAnnotation(Job.class);
//封装任务逻辑
TargetJobRun targetJobRun=new TargetJobRun(targetObj,methodProxy,params);
JobDetail jobDetail = JobBuilder.newJob(LuckyJob.class)
.withIdentity(jtname, LUCKY_JOB_GROUP)
.build();
//将任务逻辑put到上下文中
jobDetail.getJobDataMap().put(LUCKY_JOB,targetJobRun);
Trigger trigger =getTrigger(quartzJob,jtname);
scheduler.scheduleJob(jobDetail,trigger);
scheduler.start();
return null;
}
return methodProxy.invokeSuper(targetObj,params);
}
//根据@Job注解的属性构造相应的Trigger
private Trigger getTrigger(Job job,String triggerName){
final String cron = job.cron();
TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger().withIdentity(triggerName, LUCKY_JOB_GROUP);
//Cron表达式构建Trigger
if(!"".equals(cron)){
Trigger trigger = triggerBuilder.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.build();
return trigger;
}
long fixedDelay = job.fixedDelay();
int count = job.performCount();
long interval = job.performInterval();
//构建一个固定延时且只会执行一次的Trigger
if(fixedDelay!=-1L){
Trigger trigger =triggerBuilder
.startAt(futureDate((int) fixedDelay, DateBuilder.IntervalUnit.MILLISECOND))
.build();
return trigger;
}
//构建一个相隔固定时间一次执行的Trigger(永不结束)
if(count<1){
Trigger trigger =triggerBuilder
.withSchedule(simpleSchedule()
.withIntervalInMilliseconds(interval)
.repeatForever())
.build();
return trigger;
}
//构建一个相隔固定时间一次执行,且执行count次后会结束的Trigger
Trigger trigger = triggerBuilder
.withSchedule(simpleSchedule().withIntervalInMilliseconds(interval).withRepeatCount(count-1))
.build();
return trigger;
}
}
5.3 任务逻辑包装器
TargetJobRun.java
import net.sf.cglib.proxy.MethodProxy;
public class TargetJobRun {
private Object job;
private MethodProxy jobMethodProxy;
private Object[] jobParams;
public TargetJobRun(Object job, MethodProxy jobMethodProxy, Object[] jobParams) {
this.job = job;
this.jobMethodProxy = jobMethodProxy;
this.jobParams = jobParams;
}
public Object getJob() {
return job;
}
public void setJob(Object job) {
this.job = job;
}
public MethodProxy getJobMethodProxy() {
return jobMethodProxy;
}
public void setJobMethodProxy(MethodProxy jobMethodProxy) {
this.jobMethodProxy = jobMethodProxy;
}
public Object[] getJobParams() {
return jobParams;
}
public void setJobParams(Object[] jobParams) {
this.jobParams = jobParams;
}
public void run(){
try {
jobMethodProxy.invokeSuper(job,jobParams);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
5.4 Quartz Job
LuckyJob.java
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import quartz.TargetJobRun;
import static quartz.constant.Constant.LUCKY_JOB;
public class LuckyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
TargetJobRun targetJobRun = (TargetJobRun) context.getJobDetail().getJobDataMap().get(LUCKY_JOB);
targetJobRun.run();
}
}
5.5 常量
Constant.java
public class Constant {
public final static String LUCKY_JOB="501205ffeaa16b4fc18957c81d2eb283";
public final static String LUCKY_JOB_GROUP="LuckyJG";
}
六.原理
在代理方法中编写Quartz的模版代码,并将原方法的执行逻辑封装成一个TargetJobRun对象,然后将这个对象通过JobDetail对象put到JobExecutionContext上下文对象中,Quartz在运行LuckyJob的execute方法时会将这个TargetJobRun对象从上下文中取出并执行,这样就达到了业务逻辑与QuartzJob分离的目的!