quartz交流QQ群:77383408,喜欢的同行一起来探讨问题吧
最近在项目中需要用到quartz,开始使用的xml配置创建的job,一切ok。后来觉得每次添加任务都要写一大段xml,就将job放入了数据库,在spring启动时去启动数据库中保存的所有job。
其中遇到问题,无法注入spring管理的bean。本文是解决方案,研究了几天,终于找到原因了!
首先,本文实现使用的是内存型,没有持久化到数据库。
一、quartz.properties文件
#调度器名,无关紧要,名字任意定
org.quartz.scheduler.instanceName = XXScheduler
org.quartz.scheduler.instanceId = AUTO
#============================================================================
# Configure ThreadPool 配置数据库连接池
#============================================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 12
org.quartz.threadPool.threadPriority = 5
#============================================================================
# Configure JobStore 配置做业存储方式
#============================================================================
#相当于扫描频率,如果系统基于秒级,应培植成1000,quartz默认为分级(60000)
org.quartz.jobStore.misfireThreshold = 1000
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
二、创建任务Job
方法一:使用xml方式配置job
0/1 * * * * ?
好了,至此使用xml方式配置job就全部完毕了。
其中值得注意的是下面两句代码:自定义MyJobFactory,在job类里面才可以注入spring的service。
public class MyJobFactory extends AdaptableJobFactory {
// 这个对象Spring会帮我们自动注入进来,也属于Spring技术范畴.
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
// 进行注入,这属于Spring的技术,不清楚的可以查看Spring的API.
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
/**
* Job任务类,实现Job接口,可以成功注入service
*/
public class JobClassTest implements Job {
@Autowired
private ProductBaseDao productBaseDao;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
ProductBase p = productBaseDao.load(ProductBase.class, 2);
System.out.println("注入service成功!查询结果为:"+p);
}
}
至此,启动容器,job成功启动。
方式二:使用代码创建Job
表结构:本示例只需要一张表即可!
数据库有了记录,那么我们就需要在spring容器启动后,将所有记录获取出来,并通过代码创建每一个job。
在applicationContext.xml配置文件最末加上这一句,即可在spring启动后,去执行指定类中的方法。
/**
* 在Spring容器将所有的Bean都初始化完成之后的操作
*/
public class InstantiationTracingBeanPostProcessor implements ApplicationListener {
@Autowired
private TimerJobService timerJobService;
@Autowired
private MyJobFactory myJobFactory;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 避免onApplicationEvent方法被执行两次
if(event.getApplicationContext().getParent() == null){
try {
// 获取Scheduler对象,并自定义jobFactory
Scheduler scheduler = QuartzUtil.getInstance();
scheduler.setJobFactory(myJobFactory);
// 查询所有正常状态的定时任务,并在容器启动后,启动任务
List jobs = timerJobService.getNormalList();
for(TimerJob record : jobs){
Integer id = record.getId();
String name = record.getJobName()+"_"+id;
String group = record.getJobGroup()+"_"+id;
// 加载job类
Class extends Job> clazz = null;
try {
clazz = (Class extends Job>) Class.forName(record.getClassName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//生成jobDetail
JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(name, group).build();
//表达式调度构建器
CronScheduleBuilder cornSB= CronScheduleBuilder.cronSchedule(record.getCronExpression());
//生成触发器
CronTrigger trigger = (CronTrigger)TriggerBuilder.newTrigger().withIdentity(name, group).withSchedule(cornSB).build();
//添加job
scheduler.scheduleJob(jobDetail, trigger);
}
//开始执行shceduler
scheduler.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
其中创建Scheduler实例方法为:
public class QuartzUtil {
private static SchedulerFactory ssf = new StdSchedulerFactory();
/**
* 获取Scheduler实例,使用工厂模式获取
* @return
*/
public static Scheduler getInstance(){
Scheduler sched = null;
try {
sched = ssf.getScheduler();
} catch (SchedulerException e) {
e.printStackTrace();
}
return sched;
}
}
其中关键代码为:scheduler.setJobFactory(myJobFactory) ;同xml配置一样,需要指定自定义的JobFactory。
最开始,我是这样设置的:scheduler.setJobFactory(new MyJobFactory());
可是这样的结果就是,任务全部启动了,可是在job任务类注入不了bean。思考了很久,才突然发现, MyJobFactory是使用new关键字实例化出来的,在spring中,自己new出来的都不会交给spring的context去管理!!!
既然找到问题所在,解决就简单多了,只需要将MyJobFactory交给spring中去,再在上文通过注解注入即可!
在MyJobFactory.java类最上面添加@component注解,启动时spring去扫描组件会将该类注入。
@Component
public class MyJobFactory extends AdaptableJobFactory {......}
至此,问题全部解决,两种创建job的方法,个人觉得第二种比较简单。需要新增任务时,只需要在数据库添加一条记录,再添加一个对应的job类即可。
有了这一张表,同样可以配置后台页面,实现对任务的控制,这里就不讲啦!
最后,欢迎喜欢quartz的人,加入QQ群:77383408
把你的问题说出来集思广益,避免大家重蹈覆辙。