一、 quartz:
在学习quartz之前,我们先来认识一下quartz,它到底是个什么东西,我们为什么要学它;
说的比较通俗一点,quartz就是一个定时器,用来完成复杂一点的定时任务的,它的使用也是非常的简单,下边我们将由浅入深的介绍quartz这个框架;
二、quartz的一些使用属性和概念的介绍:
要了解一个框架之前,我们首先要知道它里边最基本的一些类的属性是什么:
说的比较通俗一点:
job:就是你的定时器需要完成的任务是什么;
jobDetail:有的时候,我们的job定义的任务可能会比较的抽象,这个时候jobDetail就发挥作用了,它是你的一个job的实例。job和jobDetail是不分家的。
trigger:触发器 ,他的作用是什么时候来触发这个定时任务,比如说每三天执行一次,每周末执行一次等等,触发器一共分两种,分别是简单的触发器和cron触发器;
scheduler:调度器,它的作用就是将trigger和job联系起来,因为不管怎么说,触发器还是要作用在job身上才是有用的嘛;
他们之间的关系如下图所示:
从上图我们可以看出,触发器可以绑定job(也可以用scheduler绑定),而scheduler和绑定触发器,进而将三者绑定在一起,同时呢,一个job是可以对应多个jobDetail的;
而一个jobDetail也是可以对应多个trigger的;
好的,概念扫盲暂时到这里,接下来让我们来看一点简单的实例把!
三、简单的quartz定时任务的使用:
前置pom:
org.quartz-scheduler
quartz
2.3.2
org.springframework
spring-context-support
3.1、spring的注解@Bean方式:
@Bean
public JobDetailFactoryBean jobDetailFactoryBean() {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
// 关联我们自己的job类
factoryBean.setJobClass(QuartzDemo.class);// 这里他将job对象创建仅仅是反射,没有加入到spring容器里边
return factoryBean;
}
@Bean
public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean) {
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
String cronExpress = "0/4 * * * * ? *";
factoryBean.setCronExpression(cronExpress);
return factoryBean;
}
/* * 创建scheduler对象*/
@Bean
public SchedulerFactoryBean schedulerFactoryBean(CronTriggerFactoryBean CronTriggerFactoryBean, MyAdptableFactory myAdptableFactory) {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
// 关联trigger
schedulerFactoryBean.setTriggers(CronTriggerFactoryBean.getObject());
// 用自己写的jobFactory覆盖本来的factory实现将job加入到spring容器中
schedulerFactoryBean.setJobFactory(myAdptableFactory);
return schedulerFactoryBean;
}
3.2、为什么要自己重写工厂类覆盖quartz本来的;
其中这里我们在最后创建scheduler的时候,说到了将自己写的jobFactory覆盖quartz本来的jobFactory,这是为什么呢?
其实我们的job的创建过程是在AdaptableJobFactory这个类中完成的,我们来看一下这个类的源码:
public class AdaptableJobFactory implements JobFactory {
public AdaptableJobFactory() {
}
public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
try {
Object jobObject = this.createJobInstance(bundle);
return this.adaptJob(jobObject);
} catch (Throwable var4) {
throw new SchedulerException("Job instantiation failed", var4);
}
}
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Class> jobClass = bundle.getJobDetail().getJobClass();
return ReflectionUtils.accessibleConstructor(jobClass, new Class[0]).newInstance();
}
protected Job adaptJob(Object jobObject) throws Exception {
if (jobObject instanceof Job) {
return (Job)jobObject;
} else if (jobObject instanceof Runnable) {
return new DelegatingJob((Runnable)jobObject);
} else {
throw new IllegalArgumentException("Unable to execute job class [" + jobObject.getClass().getName() + "]: only [org.quartz.Job] and [java.lang.Runnable] supported.");
}
}
}
它的重点就在createJobInstance 这个方法中,通过这个方法我们可以看到,job的创建仅仅就是一个反射的过程,并不是通过spring的动态代理代理出来的,这样的话就会造成很多问题,比如说:事务的失效,不能在job中用@AutoWire注解来注入别的类使用等等;
所以我们要自己重写他的一个实例过程,将他加入到spring容器中去;代码如下:
@Component("MyAdptableFactory")
public class MyAdptableFactory extends AdaptableJobFactory {
/**
* 改方法需要将实例话的任务对象否手动的添加到springIOC容器中并且完成对象的注入
* 因为quartz的本来的创建job只是用反射创建对象加入这种情况是不能呗spirng容器所管理的
* @param bundle
* @return
* @throws Exception
*/
//AutowireCapableBeanFactory 可以将一个对象添加到spring IOC容器中,并且完成该对象的注入
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object object=super.createJobInstance(bundle);
//将object添加到spring容器中
this.autowireCapableBeanFactory.autowireBean(object);
return object;
}
}
这样的话,我们就可以在job中用 @Autowired注解来注入别的bean了,但是需要注意即使这样,我们自己实现的job类,还是无法应用事务的,因为这是我们手动将自己创建出来的类;
3.3、原生的写法实现简单定时任务:
在这个里边我们实现了将一个job里边绑定多个触发器;
这里对其中某些方法做一些解释:
其中withIdentity(绑定身份):作用是对于你创建的触发器或者job进行命名分组,作用是好管理;
usingJobData:作用是给job或者trigger一个属性值,这个属性值你可以在job中进行调用;
withSchedule:作用是给触发器一个触发事件,有两种,分别是简单的和cron的;
getJobDataMap:也是给job或者trigger进行注入属性;
forJob:给触发器绑定一个job;
//创建一个scheduler
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.getContext().put("skey", "svalue");
//创建一个Trigger
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1") //进行触发器的命名分组
.usingJobData("t1", "第一个触发器") // 注入属性
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10)
.repeatForever()).build(); // 进行时间间隔的设置
trigger.getJobDataMap().put("t2", "tv2");
Trigger trigger1=TriggerBuilder.newTrigger()
.withIdentity("trigger2","group2")
.forJob("myjob","mygroup")
.usingJobData("t3","第二个触发")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3)
.repeatForever()).build();
Trigger trigger2=TriggerBuilder.newTrigger()
.withIdentity("trigger2","group3")
.forJob("myjob","mygroup")
.usingJobData("t3","第二次触发修改")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();
//创建一个job
JobDetail job = JobBuilder.newJob(jobDemo1.class)
.usingJobData("j1", "jv1") // 注入属性
.withIdentity("myjob", "mygroup").build();//进行job的命名分组
job.getJobDataMap().put("j2", "jv2");// 注入属性
//注册trigger并启动scheduler
scheduler.scheduleJob(job,trigger);
scheduler.scheduleJob( trigger1);
scheduler.scheduleJob(trigger2);
scheduler.start();
3.3.1、上述代码中,jobDetail和trigger都创建好了,呢么job如何创建呢?我们看下边的代码—(只演示原生写法的job)
从代码中可以看出,我们可以把创建jobDetail和trigger时注入的属性一一拿出来,其中呢,getMergedJobDataMap是综合了jobDetail和trigger的属性并集,并且优先选取trigger中的属性;
public class jobDemo1 implements Job {
public jobDemo1() {
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// Object tv1 = context.getTrigger().getJobDataMap().get("t1");
// Object tv2 = context.getTrigger().getJobDataMap().get("t2");
// Object jv1 = context.getJobDetail().getJobDataMap().get("j1");
// Object jv2 = context.getJobDetail().getJobDataMap().get("j2");
// Object sv = null;
// try {
// sv = context.getScheduler().getContext().get("skey");
// } catch (SchedulerException e) {
// e.printStackTrace();
// }
// System.out.println(tv1+":"+tv2);
// System.out.println(jv1+":"+jv2);
// System.out.println(sv);
// System.out.println("hello:"+ LocalDateTime.now());
JobKey key = context.getJobDetail().getKey();//获取jobDetail的分组和命名
TriggerKey key1 = context.getTrigger().getKey();
JobDataMap dataMap=context.getMergedJobDataMap();
String j1 = dataMap.getString("j1");
System.out.println("key = " + key);
System.out.println("key1 = " + key1);
System.out.println("dataMap = " + dataMap.get("t3"));
System.out.println("j1 = " + j1);
}
}
恭喜你,如果你看到这里的话,呢么你对quartz的基本操作已经了解的差不多了,接下来的篇幅,就让我们去了解进阶的篇幅把!