Spring爲創建Quartz的Scheduler、Trigger和JobDetail提供了便利的FactoryBean類,以便能夠在Spring 容器中享受注入的好處。此外Spring還提供了一些便利工具類直接將Spring中的Bean包裝成合法的任務。Spring進一步降低了使用Quartz的難度,能以更具Spring風格的方式使用Quartz。概括來說它提供了兩方面的支持:
1)爲Quartz的重要組件類提供更具Bean風格的擴展類;
2)提供創建Scheduler的BeanFactory類,方便在Spring環境下創建對應的組件對象,並結合Spring容器生命週期進行啓動和停止的動作。
第一步: 配置SchedulerFactoryBean
Quartz的SchedulerFactory是標準的工廠類,不太適合在Spring環境下使用。此外,爲了保證Scheduler能夠感知Spring容器的生命週期,完成自動啓動和關閉的操作,必須讓Scheduler和Spring容器的生命週期相關聯。以便在Spring容器啓動後,Scheduler自動開始工作,而在Spring容器關閉前,自動關閉Scheduler。爲此,Spring提供SchedulerFactoryBean,這個FactoryBean大致擁有以下的功能:
1)以更具Bean風格的方式爲Scheduler提供配置信息;
2)讓Scheduler和Spring容器的生命週期建立關聯,相生相息;
3)通過屬性配置部分或全部代替Quartz自身的配置文件。
spring容器中的bean只能放到SchedulerContext裏面傳入job中。
- <bean name="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >
- <!-- 注入數據源 -->
- <property name="dataSource">
- <ref bean="dataSource" />
- </property>
- <property name="transactionManager" ref="txManager">
- </property>
- <!-- 延遲30秒啓動Scheduler -->
- <property name="startupDelay" value="30"></property>
- <property name="schedulerContextAsMap">
- <map>
- <!-- spring 管理的service需要放到這裏,才能夠注入成功 -->
- <description>schedulerContextAsMap</description>
- <entry key="jobService" value-ref="jobService"/>
- </map>
- </property>
- <!-- 通過applicationContextSchedulerContextKey屬性配置spring上下文 -->
- <property name="applicationContextSchedulerContextKey">
- <value>applicationContext</value>
- </property>
- </bean>
SchedulerFactoryBean屬性介紹:
●triggers:triggers屬性爲Trigger[]類型,可以通過該屬性註冊多個Trigger
●calendars:類型爲Map,通過該屬性向Scheduler註冊Calendar;
●jobDetails:類型爲JobDetail[],通過該屬性向Scheduler註冊JobDetail;
●autoStartup:SchedulerFactoryBean在初始化後是否馬上啓動Scheduler,默認爲true。如果設置爲false,需要手工啓動Scheduler;
●startupDelay:在SchedulerFactoryBean初始化完成後,延遲多少秒啓動Scheduler,默認爲0,表示馬上啓動。如果並非馬上擁有需要執行的任務,可通過startupDelay屬性讓Scheduler延遲一小段時間後啓動,以便讓Spring能夠更快初始化容器中剩餘的Bean;
SchedulerFactoryBean的一個重要功能是允許你將Quartz配置文件中的信息轉移到Spring配置文件中,帶來的好處是,配置信息的集中化管理,同時我們不必熟悉多種框架的配置文件結構。回憶一個Spring集成JPA、Hibernate框架,就知道這是Spring在集成第三方框架經常採用的招數之一。SchedulerFactoryBean通過以下屬性代替框架的自身配置文件:
●dataSource:當需要使用數據庫來持久化任務調度數據時,你可以在Quartz中配置數據源,也可以直接在Spring中通過dataSource指定一個Spring管理的數據源。如果指定了該屬性,即使quartz.properties中已經定義了數據源,也會被此dataSource覆蓋;
●transactionManager:可以通過該屬性設置一個Spring事務管理器。在設置dataSource時,Spring強烈推薦你使用一個事務管理器,否則數據表鎖定可能不能正常工作;
●nonTransactionalDataSource:在全局事務的情況下,如果你不希望Scheduler執行化數據操作參與到全局事務中,則可以通過該屬性指定數據源。在Spring本地事務的情況下,使用dataSource屬性就足夠了;
●quartzProperties:類型爲Properties,允許你在Spring中定義Quartz的屬性。其值將覆蓋quartz.properties配置文件中的設置,這些屬性必須是Quartz能夠識別的合法屬性,在配置時,你可以需要查看Quartz的相關文檔。
配置好數據源dataSource後,需要在Quartz的QRTZ_LOCKS表中插入以下數據:
INSERT INTO QRTZ_LOCKS values('TRIGGER_ACCESS');
INSERT INTO QRTZ_LOCKS values('JOB_ACCESS');
否則會報
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scheduler' defined in file [...\webapps\WEB-INF\classes\config\applicationContext-quartz.xml]: Invocation of init method failed; nested exception is org.quartz.SchedulerConfigException: Failure occured during job recovery. [See nested exception: org.quartz.impl.jdbcjobstore.LockException: Failure obtaining db row lock: No row exists in table QRTZ_LOCKS for lock named: TRIGGER_ACCESS [See nested exception: java.sql.SQLException: No row exists in table QRTZ_LOCKS for lock named: TRIGGER_ACCESS]]異常
第二步 動態添加Job
- JobDetail jobDetail = new JobDetail("jobName", "jobGroup",
- TestJob.class);
- jobDetail.setDescription("Description");
- CronTrigger trigger = new CronTrigger("TriggerName",
- "TriggerGroup", "CronContent");
- try {
- if (!scheduler.isStarted()) {
- scheduler.start();
- }
- scheduler.scheduleJob(jobDetail, trigger);
- catch (SchedulerException e) {
- }
第三步 實現Job實現類
- public class TestJob extends QuartzJobBean {
- @Override
- protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
- Scheduler scheduler = (Scheduler) context.getScheduler();
- String jobName = context.getJobDetail().getName();
- String jobGroup = context.getJobDetail().getGroup();
- //獲取JobExecutionContext中的service對象
- try {
- //獲取JobExecutionContext中的service對象
- SchedulerContext schCtx = context.getScheduler().getContext();
- //獲取Spring中的上下文
- ApplicationContext appCtx = (ApplicationContext)schCtx.get("applicationContext");
- jobService= (JobService)appCtx.getBean("jobService");
- ....
- } catch (SchedulerException e1) {
- // TODO 尚未處理異常
- e1.printStackTrace();
- }
- }
- };