例子1:java例子
Job是Quartz中的一个接口,接口下只有execute方法,在这个方法中编写业务逻辑。
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("任务"+ DateUtil.now());
}
}
每隔两秒调度执行例子
public class MyScheduler {
public static void main(String[] args) throws Exception {
// 创建一个JobDetail实例,将该实例与MyJob Class绑定
JobDetail jobDetail = JobBuilder.newJob(MyJob.class).withIdentity("myJob", "group").build();
//构建Trigger实例,每隔2s执行一次
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "group")
.withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ? ")).build();
//从调度程序工厂获取一个调度程序的实例
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
//告诉quartz使用定义的触发器trigger安排执行任务job
scheduler.scheduleJob(jobDetail, cronTrigger);
//启动任务调度程序,内部机制是线程的启动
scheduler.start();
}
}
结果
任务2020-11-28 15:26:14
任务2020-11-28 15:26:16
任务2020-11-28 15:26:18
任务2020-11-28 15:26:20
任务2020-11-28 15:26:22
......
结论
JobDetail用来绑定Job,为Job实例提供许多属性:
JobDetail绑定指定的Job,每次Scheduler调度执行一个Job的时候,首先会拿到对应的Job,然后创建该Job实例,再去执行Job中的execute()的内容,任务执行结束后,关联的Job对象实例会被释放,且会被JVM GC清除。
为什么设计成JobDetail + Job,不直接使用Job
JobDetail定义的是任务数据,而真正的执行逻辑是在Job中。这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,Sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题。
Trigger是Quartz的触发器,会去通知Scheduler何时去执行对应Job。
例子2:传参 JobExecutionContext和JobDataMap
在创建JobDetail和Trigger时可以给JobExecutionContext传入以下几个类型参数,让定时任务拿到这些参数
String,Integer,Long,Float,Double,Boolean,JobDataMap(Map实现类)
public class MyScheduler {
public static void main(String[] args) throws Exception {
// 创建一个JobDetail实例,将该实例与MyJob Class绑定
JobDetail jobDetail = JobBuilder.newJob(MyJob.class).withIdentity("myJob", "group")
//传入自定义参数
.usingJobData("float",1f)
.usingJobData("String","字符串1")
.build();
//构建Trigger实例,每隔2s执行一次
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "group")
.withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ? "))
//传入自定义参数
.usingJobData("float",2f)
.usingJobData("String","字符串2")
.build();
//从调度程序工厂获取一个调度程序的实例
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
//告诉quartz使用定义的触发器trigger安排执行任务job
scheduler.scheduleJob(jobDetail, cronTrigger);
//启动任务调度程序,内部机制是线程的启动
scheduler.start();
}
}
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
//获取合并后的JobDataMap,但是相同key的value会被trigger覆盖
//可以用context.getJobDetail()/context.getTrigger()来分别获取key/value
JobDataMap map = context.getMergedJobDataMap();
String[] keys = map.getKeys();
for (String key : keys) {
System.out.println(key+map.get(key));
}
System.out.println("任务"+ DateUtil.now());
}
}
结论
JobExecutionContext是什么
- 当Scheduler调用一个Job,就会将JobExecutionContext传递给Job的execute()方法;
- Job能通过JobExecutionContext对象访问到Quartz运行时候的环境以及Job本身的明细数据。
JobDataMap是什么?
- 在进行任务调度时JobDataMap存储在JobExecutionContext中,非常方便获取
- JobDataMap可以用来装载任何可以序列化的数据对象,当job实例对象被执行时这些参数对象会传递给它
- JobDataMap实现了JDK的Map接口,并且添加了一些非常方便的方法用来存取数据基本数据类型。
例子3:Spring整合Quartz
@Component("myWork")
public class MyWork {
public void add(){
System.out.println("add"+ DateUtil.now());
}
}
<!-- 自定义任务 -->
<bean id="myDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<!--定时任务目标类-->
<property name="targetObject" ref="myWork"></property>
<!--要定时的方法-->
<property name="targetMethod" value="add"></property>
<property name="concurrent" value="false"/>
</bean>
<!-- 调度工厂 -->
<bean id="scheduler" lazy-init="true" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
<ref bean="myDetail"/>
</list>
</property>
<property name="triggers">
<list>
<ref bean="myTrigger"/>
</list>
</property>
</bean>
<!-- 定义触发时间 -->
<bean id="myTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="myDetail"></property>
<property name="cronExpression">
<!-- 每两秒执行 -->
<value>0/2 * * * * ?</value>
</property>
</bean>
启动tomcat
add2020-11-28 19:40:36
add2020-11-28 19:40:38
add2020-11-28 19:40:40
add2020-11-28 19:40:42
......
结论
Spring创建JobDetail的两种方式
配置Spring的任务调度抽象层简化了任务调度,在Quartz的基础上提供了更好的调度对象。Spring使用Quartz框架来完成任务调度,创建Quartz的作业Bean(JobDetail),有以下两种方法:
- 利用JobDetailBean包装QuartzJobBean子类(即Job类)的实例。
- 利用MethodInvokingJobDetailFactoryBean工厂Bean包装普通的Java对象(即Job类)。
说明
采用第一种方法 创建job类,一定要继承QuartzJobBean ,实现 executeInternal(JobExecutionContext jobexecutioncontext)方法,此方法就是被调度任务的执行体,然后将此Job类的实例直接配置到JobDetailBean中即可。这种方法和在普通的Quartz编程中是一样的。
采用第二种方法 创建Job类,无须继承父类,直接配置MethodInvokingJobDetailFactoryBean即可。但需要指定一下两个属性:
- targetObject:指定包含任务执行体的Bean实例。
- targetMethod:指定将指定Bean实例的该方法包装成任务的执行体。
- concurrent:是否并发进行,false,表示不可以并发,其他任务将在当前任务完成后在调用。