最近在項目中須要用到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
把你的問題說出來集思廣益,避免你們重蹈覆轍。