Spring集成Quartz---具體場景應用

  Java併發編程階段,突然接觸到了定時調度任務這一概念,恰巧在實習期間有過運用,因此特寫此博客以記錄,歡迎指正和討論。
                         ——前言

本文將分以下四點進行討論。篇幅過長的話,可能分其他章節。

Quartz基本概念

一、quartz核心概念
先來看一張圖:
這裏寫圖片描述
對於上圖中各個部分的概念解釋如下:
scheduler:任務調度器
trigger:觸發器,用於定義任務調度時間規則
job:任務,即被調度的任務
misfire:錯過的,指本來應該被執行但實際沒有被執行的任務調度

Job:是一個接口,只有一個方法void execute(JobExecutionContext context),開發者實現該接口定義運行任務,JobExecutionContext類提供了調度上下文的各種信息。Job運行時的信息保存在JobDataMap實例中;

JobDetail:Quartz在每次執行Job時,都重新創建一個Job實例,所以它不直接接受一個Job的實例,相反它接收一個Job實現類,以便運行時通過newInstance()的反射機制實例化Job。因此需要通過一個類來描述Job的實現類及其它相關的靜態信息,如Job名字、描述、關聯監聽器等信息,JobDetail承擔了這一角色。

Trigger:是一個類,描述觸發Job執行的時間觸發規則。主要有SimpleTrigger和CronTrigger這兩個子類。當僅需觸發一次或者以固定時間間隔週期執行,SimpleTrigger是最適合的選擇;而CronTrigger則可以通過Cron表達式定義出各種複雜時間規則的調度方案:如每早晨9:00執行,週一、週三、週五下午5:00執行等;

Calendar:org.quartz.Calendar和java.util.Calendar不同,它是一些日曆特定時間點的集合(可以簡單地將org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一個日曆時間點,無特殊說明後面的Calendar即指org.quartz.Calendar)。一個Trigger可以和多個Calendar關聯,以便排除或包含某些時間點。假設,我們安排每週星期一早上10:00執行任務,但是如果碰到法定的節日,任務則不執行,這時就需要在Trigger觸發機制的基礎上使用Calendar進行定點排除。

Scheduler:代表一個Quartz的獨立運行容器,Trigger和JobDetail可以註冊到Scheduler中,兩者在Scheduler中擁有各自的組及名稱,組及名稱是Scheduler查找定位容器中某一對象的依據,Trigger的組及名稱必須唯一,JobDetail的組和名稱也必須唯一(但可以和Trigger的組和名稱相同,因爲它們是不同類型的)。Scheduler定義了多個接口方法,允許外部通過組及名稱訪問和控制容器中Trigger和JobDetail。
Scheduler可以將Trigger綁定到某一JobDetail中,這樣當Trigger觸發時,對應的Job就被執行。一個Job可以對應多個Trigger,但一個Trigger只能對應一個Job。可以通過SchedulerFactory創建一個Scheduler實例。Scheduler擁有一個SchedulerContext,它類似於ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以訪問SchedulerContext內的信息。SchedulerContext內部通過一個Map,以鍵值對的方式維護這些上下文數據,SchedulerContext爲保存和獲取數據提供了多個put()和getXxx()的方法。可以通過Scheduler# getContext()獲取對應的SchedulerContext實例;

ThreadPool:Scheduler使用一個線程池作爲任務運行的基礎設施,任務通過共享線程池中的線程提高運行效率。
PS:這裏的線程池是實現定時任務的關鍵。

Quartz集成Spring

由於項目中用到了定時任務調度,因此這裏以項目爲例,脫敏之後附上代碼。
一、添加Maven依賴

<dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
                <version>2.2.2</version>
                <exclusions>
                    <exclusion>
                        <artifactId>slf4j-api</artifactId>
                        <groupId>org.slf4j</groupId>
                    </exclusion>
                </exclusions>
            </dependency>

二、Spring Bean配置
抓住以下幾點:
首先需要定義一個線程池的Bean,用來運行定時任務。

<bean id="taskThreadPool" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <!-- 核心線程數 -->
        <property name="corePoolSize" value="20"/>
        <!-- 最大線程數 -->
        <property name="maxPoolSize" value="200"/>
        <!-- 隊列最大長度 -->
        <property name="queueCapacity" value="500"/>
        <!-- 線程池維護線程所允許的空閒時間 -->
        <property name="keepAliveSeconds" value="1800"/>
        <!-- 線程池對拒絕任務(無線程可用)的處理策略 -->
        <property name="rejectedExecutionHandler">
            <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"/>
        </property>
    </bean>

而後,需要定義兩個Bean:觸發器(CronTrigger)和與之綁定的作業(JobDetail)
CronTrigger與JobDetail是組合關係,一個CronTrigger維護一個JonDetail

 <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"
          abstract="true"/>

    <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"
          abstract="true">
        <property name="targetMethod" value="execute"/>
        <property name="concurrent" value="true"/>
    </bean>

注意,這裏定義的JobDetail並沒有指定屬性targetObject,主要是爲了後續方便複用。
最後,需要定一個存放觸發器的容器Schedule的Bean

<bean id="workTaskScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="taskExecutor" ref="taskThreadPool"/>
        <property name="autoStartup" value="true"/>
        <property name="triggers">
            <list>
                <bean parent="cronTrigger">
                    <property name="jobDetail">
                        <bean parent="jobDetail">
                            <property name="targetObject" ref="MyTaskA"/>
                        </bean>
                    </property>
                    <property name="cronExpression" value="0/5 * * * * ?"/>
                </bean>
                <bean parent="cronTrigger">
                    <property name="jobDetail">
                        <bean parent="jobDetail">
                            <property name="targetObject" ref="MyTaskB"/>
                        </bean>
                    </property>
                    <property name="cronExpression" value="0/5 * * * * ?"/>
                </bean>
                <bean parent="cronTrigger">
                    <property name="jobDetail">
                        <bean parent="jobDetail">
                            <property name="targetObject" ref="MyTaskC"/>
                        </bean>
                    </property>
                    <property name="cronExpression" value="0/5 * * * * ?"/>
                </bean>
                ......
                ......
            </list>
        </property>
    </bean>

結構很簡單,如果需要在Schedule中配置多個Trigger,只需要用List進行配置即可。
這裏未給出Bean:MyTaskA,MyTaksB,MyTaskC的具體定義,可以參考幾個簡單的bean定義(畢竟這些東西網上一搜一大把)
以上三步驟就完成了Spring集成Quartz的整個過程。

發佈了54 篇原創文章 · 獲贊 87 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章