Quartz Scheduler與Spring集成(一) 基礎配置與常見問題


測試環境 Spring3.x Quartz-2.1.7

Spring提供了很多工具類與Quartz框架集成,對集成實現了很好的支持。關於Quartz的技術細節這里不解釋,這里只是講集成的方案,並且需要對Quartz框架很了解的情況才能理解一些細節的東西。

首先讓門們先認識一下Spring提供給我們的4個工具類。

 

這個類提供了對org.quartz.Scheduler的創建與配置,並且會管理它的生命周期與Spring同步。

org.springframework.scheduling.quartz.SchedulerFactoryBean

這個類提供創建JobDetail 並提供缺省配置。

org.springframework.scheduling.quartz.JobDetailFactoryBean

這個類提供創建SimpleTrigger 並提供缺省配置。

org.springframework.scheduling.quartz.SimpleTriggerFactoryBean

這個類提供創建CronTrigger 並提供缺省配置。

org.springframework.scheduling.quartz.CronTriggerFactoryBean

 

利用Spring提供的4個BeanFactory工具類,我們就可以對Quartz進行集成了。

首先需要在Spring的xml當中把任務配置進去

    <bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
      <!-- 這里是你的具體任務 -->
      <property name="jobClass" value="com.gary.operation.jobdemo.demo1.HelloJob" />
      <!-- 這里要設置為true -->
      <property name="durability" value="true"></property>
    </bean>

任務實現類

public class HelloJob implements Job { @Autowired private SqlSession sqlSession; private static Logger _log = LoggerFactory.getLogger(HelloJob.class);public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("HelloJob.execute()------------------------------------"); } }

接下來我們配置一個Trigger, 這里我們使用SimpleTrigger那么我們需要使用SimpleTriggerFactoryBean來創建它

配置的細節不解釋 都是SimpleTrigger當中的屬性,配置任務什么時間觸發,時間間隔等。

    <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
        <property name="jobDetail" ref="exampleJob" />
        <property name="startDelay" value="1000" />
        <property name="repeatInterval" value="3000" />
        <property name="repeatCount" value="1000"/>
    </bean>

 

最后我們配置Scheduler來把任務加進去,所以我們使用SchedulerFactoryBean。

先來看一下quartz.properties由於我們使用Spring提供的數據源,所以JobStore我們不用配置。

org.quartz.scheduler.instanceName = MyScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.scheduler.skipUpdateCheck = true

#============================================================================
# Configure ThreadPool  
#============================================================================
org.quartz.threadPool.threadCount = 3
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadPriority: 5

#============================================================================
# Configure JobStore  
#============================================================================
#org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT
#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
#org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.MSSQLDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.useProperties = false
#org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.misfireThreshold = 6000

 

在看Spring.xml

    <bean name="MyScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="dataSource" ref="c3p0DataSource" /> <!-- 這里就設置Spring的數據源就行了 -->
         <property name="schedulerName" value="MyScheduler"></property>
         <property name="overwriteExistingJobs" value="true"></property>
        <property name="configLocation" value="classpath:quartz.properties" />
        <property name="jobFactory" ref="jobFactory"></property>
        <property name="triggers">    
            <list>
                <ref bean="simpleTrigger"/>
            </list>
        </property>
        <property name="jobDetails">
            <list>
                <ref bean="exampleJob"/>
            </list>
        </property>
    </bean>

 

上面這樣就完成了基礎的配置,那么如果我們需要動態的添加一些任務該如何做呢?

public class XXService {

    @Autowired
    private Scheduler scheduler;
    
    @Transactional
    public void test() throws SchedulerException {
        JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1")
                .usingJobData("key", "jack") .usingJobData("double", 13.5)
                .build();
        Trigger trigger = newTrigger().withIdentity("trigger1", "group1")
                // .startAt(DateBuilder.futureDate(1, IntervalUnit.MINUTE))
                .startNow()
                .forJob(job)
                .withSchedule(
                        SimpleScheduleBuilder.simpleSchedule()
                                .withIntervalInSeconds(2).withRepeatCount(2))
                .build();
        scheduler.scheduleJob(job, trigger);
    }
}

其實跟正常使用quartz沒有任何區別,只要在需要用Scheduler的地方進行 Autowired就行了,Spring與自動為我們注入進來。

然后我們可以使用Spring的事物@Transactional,這樣任務可以與一些其他的業務方法在同一個事物中,是不是很方便。

還有另外一個需要注意的問題就是如果在 quartz.properties 里面配置了 org.quartz.jobStore.useProperties = true 的話 在啟動Spring的時候會拋出序列化失敗的異常,其原因是這樣的。

org.quartz.jobStore.useProperties = true 設置為true的時候 那么在給任務添加數據的時候就只能是字符串,在上面紅色標記的地方,不能出現其他類型,但是在使用SimpleTriggerFactoryBean構建Tirgger的時候它卻往里面放了引用類型,所以會導致序列化失敗,源代碼如下:

    public void afterPropertiesSet() throws ParseException { if (this.name == null) { this.name = this.beanName; } if (this.group == null) { this.group = Scheduler.DEFAULT_GROUP; } if (this.jobDetail != null) { this.jobDataMap.put(JobDetailAwareTrigger.JOB_DETAIL_KEY, this.jobDetail); } if (this.startDelay > 0) { this.startTime = new Date(System.currentTimeMillis() + this.startDelay); } else if (this.startTime == null) { this.startTime = new Date(); } /* SimpleTriggerImpl sti = new SimpleTriggerImpl(); sti.setName(this.name); sti.setGroup(this.group); sti.setJobKey(this.jobDetail.getKey()); sti.setJobDataMap(this.jobDataMap); sti.setStartTime(this.startTime); sti.setRepeatInterval(this.repeatInterval); sti.setRepeatCount(this.repeatCount); sti.setPriority(this.priority); sti.setMisfireInstruction(this.misfireInstruction); this.simpleTrigger = sti; */ Class simpleTriggerClass; Method jobKeyMethod; try { simpleTriggerClass = getClass().getClassLoader().loadClass("org.quartz.impl.triggers.SimpleTriggerImpl"); jobKeyMethod = JobDetail.class.getMethod("getKey"); } catch (ClassNotFoundException ex) { simpleTriggerClass = SimpleTrigger.class; jobKeyMethod = null; } catch (NoSuchMethodException ex) { throw new IllegalStateException("Incompatible Quartz version"); } BeanWrapper bw = new BeanWrapperImpl(simpleTriggerClass); MutablePropertyValues pvs = new MutablePropertyValues(); pvs.add("name", this.name); pvs.add("group", this.group); if (jobKeyMethod != null) { pvs.add("jobKey", ReflectionUtils.invokeMethod(jobKeyMethod, this.jobDetail)); } else { pvs.add("jobName", this.jobDetail.getName()); pvs.add("jobGroup", this.jobDetail.getGroup()); } pvs.add("jobDataMap", this.jobDataMap); pvs.add("startTime", this.startTime); pvs.add("repeatInterval", this.repeatInterval); pvs.add("repeatCount", this.repeatCount); pvs.add("priority", this.priority); pvs.add("misfireInstruction", this.misfireInstruction); bw.setPropertyValues(pvs); this.simpleTrigger = (SimpleTrigger) bw.getWrappedInstance(); }

上面紅色的地方看到了嗎,他把jobDetail也放了進去,因為設置了useProperties = true 所以jobDataMap

里面只能存放字符串所以會導致序列化失敗,解決辦法就是我們復寫這個方法,把它刪掉就行了。

public class CustomSimpleTriggerFactoryBean extends SimpleTriggerFactoryBean { @Override public void afterPropertiesSet() throws java.text.ParseException { super.afterPropertiesSet(); getJobDataMap().remove(JobDetailAwareTrigger.JOB_DETAIL_KEY); } }

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM