使用quartz步骤很简单,概括来说,分为三步;
1)job - 任务 - 你要做什么事?
2)Trigger - 触发器 - 你什么时候去做?
3)Scheduler - 任务调度 - 你什么时候需要去做什么事?
例如:
public class QuartzMain {
public static void main(String[] args) throws Exception {
// 1.创建 Job 对象: 你要做什么事?
JobDetail job = JobBuilder.newJob(QuartzDemo .class).build();
/**
* 简单的 trigger 触发时间: 通过 Quartz 提供一个方法来完成简单的重复
调用 cron
* Trigger: 按照 Cron 的表达式来给定触发的时间
*/
// 2.创建 Trigger 对象: 在什么时间做?
// Trigger trigger =TriggerBuilder.newTrigger().withSchedule(SimpleScheduleBuilder.repeatSecondlyForever()).build();
Trigger trigger = TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")).build();
// 3.创建 Scheduler 对象: 在什么时间做什么事?
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(job, trigger);
//启动
scheduler.start();
}
}
注意:QuartzDemo 就是我们自定义的job任务
/**
* 定义任务类
* * *
/
public class QuartzDemo implements Job {
/**
* 任务被触发时所执行的方法
*/
public void execute(JobExecutionContext arg0) throws
JobExecutionException {
System.out.println("Execute...."+new Date());
}
}
咱们需要引入三个坐标,如下;
org.quartz-scheduler
quartz
2.2.1
slf4j-api
org.slf4j
org.springframework
spring-context-support
org.springframework
spring-tx
/**Job类
* @author Administrator
*
*/
public class MyTask implements Job{
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("任务被执行:" + new Date());
}
}
package com.bjc.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
import com.bjc.scheduled.MyTask;
/**quartz的配置类
* @author Administrator
*
*/
@Configuration
public class Quartzconfig {
/**
* 1. 创建Job对象
* */
@Bean
public JobDetailFactoryBean jobDetailFactoryBean() {
JobDetailFactoryBean factory = new JobDetailFactoryBean();
// 关联Job任务类
factory.setJobClass(MyTask.class);
return factory;
}
/**
* 2. 创建简单的trigger对象
* */
@Bean
public SimpleTriggerFactoryBean simpleFactory(JobDetailFactoryBean factory) {
SimpleTriggerFactoryBean simple = new SimpleTriggerFactoryBean();
// 2.1 关联JobDetail对象
simple.setJobDetail(factory.getObject());
// 2.2 设置触发时间 参数表示一个执行的毫秒数
simple.setRepeatInterval(2000);
// 可选操作
// 设置重复次数
simple.setRepeatCount(10);
return simple;
}
/**
* 3. 创建scheduler对象
* */
@Bean
public SchedulerFactoryBean schedulerFactoryBean(SimpleTriggerFactoryBean triggerFactory) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
// 1. 关联trigger
factory.setTriggers(triggerFactory.getObject());
return factory;
}
}
在启动类上加上注解@EnableScheduling
如图:
运行启动类,定时任务就启动了。
上例中使用的是简单的定时器,要实现cron定时器很简单
1)将配置对象的第二步替换成下面的代码即可,例如;
@Bean
public CronTriggerFactoryBean cronTrigger(JobDetailFactoryBean factory) {
CronTriggerFactoryBean cron = new CronTriggerFactoryBean();
// 2.1 关联jobDetail
cron.setJobDetail(factory.getObject());
// 2.2 设置触发时间
cron.setCronExpression("0/2 * * * * ?");
return cron;
}
2)将第三步中的参数替换成CronTriggerFactoryBean
运行程序,定时器正常运行
我们编写一个service类,在任务类注入该service,如图:
运行,发现报了空指针异常,
这是为什么了?为什么这里service没有注入进去了?这是因为在我们的quartz配置对象中,在setJobClass实例化我们的job任务的时候,使用的是一个适配器类AdaptableJobFactory,在这里有一个方法
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
return bundle.getJobDetail().getJobClass().newInstance();
}
我们可以看到,这个方法很简单,就是拿我们传递过来的字节码通过反射的机制创建的对象,这也就意味着,现在我们的任务类并没有被spring管理,也就是说spring的IOC容器并没有管理我们的任务类,所以就无法完成注入了,所以导致null异常的出现。因为spring的注入要求注入的对象与被注入的对象均在springIOC容器中存在。那么,我们怎么将我们的人物类交给spring容器管理了?我们可以自定义一个适配器工厂类,继承AdaptableJobFactory,重写createJobInstance方法,然后使用AutowireCapableBeanFactory将创建的对象对象手动注入到spring容器中,例如:
package com.bjc.scheduled;
import javax.annotation.Resource;
import org.quartz.Job;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
// 加上@Component注解,让该适配器对象实例化
@Component("myAdaptableJobFactory")
public class MyAdaptableJobFactory extends AdaptableJobFactory{
// 该对象可以将一个对象添加到Spring的IOC容器中,并且完成该对象的属性注入
@Resource
private AutowireCapableBeanFactory autowireCapableBeanFactory;
/**
* 该方法需要将实例化的任务对象手动的添加到IOC容器中,并完成对象的注入
*/
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Job newInstance = bundle.getJobDetail().getJobClass().newInstance();
// 将创建的对象添加到SpringIOC容器中,并完成属性注入
autowireCapableBeanFactory.autowireBean(newInstance);
// 返回对象
return newInstance;
}
}
重写了适配器工厂类,怎么使用了?SchedulerFactoryBean对象允许重新设置一个工厂类,例如:
/**
* 3. 创建scheduler对象
* */
@Bean
public SchedulerFactoryBean schedulerFactoryBean(CronTriggerFactoryBean triggerFactory,MyAdaptableJobFactory myAdaptableJobFactory) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
// 1. 关联trigger
factory.setTriggers(triggerFactory.getObject());
// 2. 重新设置工厂类
factory.setJobFactory(myAdaptableJobFactory);
return factory;
}
注意:schedulerFactoryBean方法新增一个参数,参数就是我们自定义的适配器工厂类,然后通过setJobFactory将该参数对象设置进去。