最近在项目中须要用到quartz,开始使用的xml配置建立的job,一切ok。后来以为每次添加任务都要写一大段xml,就将job放入了数据库,在spring启动时去启动数据库中保存的全部job。java
其中遇到问题,没法注入spring管理的bean。本文是解决方案,研究了几天,终于找到缘由了!spring
首先,本文实现使用的是内存型,没有持久化到数据库。数据库
#调度器名,可有可无,名字任意定 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
<!-- 自定义quartz的jobFactory,将spring管理bean放到jobFactory,这样才能在job类里面经过注解注入bean --> <bean id="jobFactory" class="com.cdrzt.pcs.timer.factory.MyJobFactory"></bean> <!-- 总管理类 若是将lazy-init='false'那么容器启动就会执行调度程序 --> <bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="jobFactory" ref="jobFactory"></property> <property name="configLocation" value="classpath:quartz.properties" /> <!-- 管理trigger --> <property name="triggers"> <list> <ref bean="trigger_1"/> </list> </property> </bean> <!-- ********定时器1 ******** --> <!-- 定义jobDetail --> <bean id="detail_1" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass" value="com.xx.xx.xx.JobClassTest" /><!-- 这里指定job任务类 --> <property name="durability" value="true" /> <property name="group" value="group" /> <property name="name" value="name" /> </bean> <!-- 定义trigger --> <bean id="trigger_1" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail" ref="detail_1"></property> <property name="cronExpression"> <value>0/1 * * * * ?</value><!-- 测试使用每一秒运行一次 --> </property> </bean>
好了,至此使用xml方式配置job就所有完毕了。app
其中值得注意的是下面两句代码:自定义MyJobFactory,在job类里面才能够注入spring的service。ide
<bean id="jobFactory" class="com.cdrzt.pcs.timer.factory.MyJobFactory"></bean>
<property name="jobFactory" ref="jobFactory"></property>
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成功启动。测试
表结构:本示例只须要一张表便可!ui
数据库有了记录,那么咱们就须要在spring容器启动后,将全部记录获取出来,并经过代码建立每个job。spa
<!-- 在Spring容器将全部的Bean都初始化完成以后的操做 --> <bean class="com.xx.xx.timer.InstantiationTracingBeanPostProcessor"/>
在applicationContext.xml配置文件最末加上这一句,便可在spring启动后,去执行指定类中的方法。code
/** * 在Spring容器将全部的Bean都初始化完成以后的操做 */ public class InstantiationTracingBeanPostProcessor implements ApplicationListener<ContextRefreshedEvent> { @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<TimerJob> 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实例方法为:component
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
把你的问题说出来集思广益,避免你们重蹈覆辙。