實際業務常有順序依賴:如資料遷移需先導入主檔(被參考資料),再導入明細檔(關聯資料)。Quartz TriggerListener 可在觸發器完成時自動排程下一個作業,實現聲明式作業鏈。
public class NextJobTriggerListener extends TriggerListenerSupport {
private Logger logger = Logger.getLogger(getClass());
private String name;
private SimpleTriggerBean nextTrigger; // 要鏈接的下一個觸發器
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public void setNextTrigger(SimpleTriggerBean nextTrigger) { this.nextTrigger = nextTrigger; }
@Override
public void triggerComplete(Trigger trigger, JobExecutionContext context, int code) {
try {
Scheduler scheduler = context.getScheduler();
JobDetail nextJob = nextTrigger.getJobDetail();
Trigger oldTrigger = scheduler.getTrigger(nextTrigger.getName(), nextTrigger.getGroup());
JobDetail oldJob = scheduler.getJobDetail(nextJob.getName(), nextJob.getGroup());
if (oldJob == null && oldTrigger == null) { // 尚未註冊
logger.debug("Scheduling next job: " + code);
scheduler.scheduleJob(nextJob, nextTrigger);
} else {
logger.debug("Job/Trigger already exists, skip scheduling");
}
} catch (Exception e) {
e.printStackTrace();
}
super.triggerComplete(trigger, context, code);
}
}
關鍵邏輯:檢查目標 Job/Trigger 是否已存在於 Scheduler,避免重複註冊導致異常。
<!-- 主作業 -->
<bean id="mainJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="jobs.MainJob"/>
</bean>
<!-- 主作業觸發器,掛載 Listener -->
<bean id="mainTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="mainJob"/>
<property name="startDelay" value="6000"/>
<property name="repeatCount" value="0"/>
<property name="repeatInterval" value="3600000"/>
<property name="triggerListenerNames">
<list><value>mainTriggerListener</value></list>
</property>
</bean>
<!-- 監聽器,指定下一個觸發器 -->
<bean id="mainTriggerListener" class="listeners.NextJobTriggerListener">
<property name="name" value="mainTriggerListener"/>
<property name="nextTrigger" ref="secondTrigger"/>
</bean>
<!-- 後續作業 -->
<bean id="secondJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="jobs.SecondJob"/>
</bean>
<!-- 後續觸發器(不立即排程,由 Listener 動態添加) -->
<bean id="secondTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="secondJob"/>
<property name="startDelay" value="6000"/>
<property name="repeatCount" value="0"/>
<property name="repeatInterval" value="6000"/>
</bean>
<!-- Scheduler 工廠,註冊主觸發器與監聽器 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list><ref bean="mainTrigger"/></list>
</property>
<property name="triggerListeners">
<list><ref bean="mainTriggerListener"/></list>
</property>
<!-- quartzProperties 同前例 -->
</bean>
DEBUG [MainJob.executeInternal] Just say hi.
DEBUG [NextJobTriggerListener.triggerComplete] inside scheduleJob.3
DEBUG [SecondJob.executeInternal] I'm the second job.
DEBUG [NextJobTriggerListener.triggerComplete] oldJob==null:false
DEBUG [NextJobTriggerListener.triggerComplete] oldTrigger==null:false
mainTrigger 註冊到 SchedulerMainJob 執行mainTrigger 完成,TriggerListener 檢測並動態排程 secondTriggerSecondJob 執行mainTrigger 再次觸發時,檢測到 SecondJob 已存在,跳過排程triggerComplete 的 code 參數指示完成狀態(正常/錯誤/否決),可據此決定是否啟動下一個作業SecondJob 的 Trigger 同樣可掛載 Listener 鏈接第三個作業JobDataMap 在作業間傳遞業務資料JobChaining 內建功能,Spring 4.1+ 亦有 JobBuilder 流式 API透過 TriggerListener 實現作業鏈,保持配置聲明式、邏輯集中於 Listener、避免硬編碼排程依賴。適合資料遷移、批次處理流水線等有嚴格執行順序的業務場景。