Quartz

quartz原理
quartz

Quartz調度核心元素

  • Scheduler:任務調度器,是實際執行任務調度的控制器。在spring中通過SchedulerFactoryBean封裝起來。

  • Trigger:觸發器,用于定義任務調度的時間規則,有SimpleTrigger,CronTrigger,DateIntervalTrigger和NthIncludedDayTrigger,其中CronTrigger用的比較多,本文主要介紹這種方式。CronTrigger在spring中封裝在CronTriggerFactoryBean中。
    Calendar:它是一些日歷特定時間點的集合。一個trigger可以包含多個Calendar,以便排除或包含某些時間點。

  • JobDetail:用來描述Job實現類及其它相關的靜態信息,如Job名字、關聯監聽器等信息。在spring中有JobDetailFactoryBean和 MethodInvokingJobDetailFactoryBean兩種實現,如果任務調度只需要執行某個類的某個方法,就可以通過MethodInvokingJobDetailFactoryBean來調用。

  • Job:是一個接口,只有一個方法void execute(JobExecutionContext context),開發者實現該接口定義運行任務,JobExecutionContext類提供了調度上下文的各種信息。Job運行時的信息保存在JobDataMap實例中。實現Job接口的任務,默認是無狀態的,若要將Job設置成有狀態的,在quartz中是給實現的Job添加@DisallowConcurrentExecution注解(以前是實現StatefulJob接口,現在已被Deprecated),在與spring結合中可以在spring配置文件的job detail中配置concurrent參數。

Quartz 集群

quartz集群是通過數據庫表來感知其他的應用的,各個節點之間并沒有直接的通信。只有使用持久的JobStore才能完成Quartz集群。
數據庫表:有11張表,表信息如下

image.png

image.png

QRTZ_LOCKS就是Quartz集群實現同步機制的行鎖表,包括以下幾個鎖:CALENDAR_ACCESS 、JOB_ACCESS、MISFIRE_ACCESS 、STATE_ACCESS 、TRIGGER_ACCESS

數據存儲

Quartz 中的 trigger 和 job 需要存儲下來才能被使用。Quartz 中有兩種存儲方式:RAMJobStore, JobStoreSupport,其中 RAMJobStore 是將 trigger 和 job 存儲在內存中,而 JobStoreSupport 是基于 jdbc 將 trigger 和 job 存儲到數據庫中。RAMJobStore 的存取速度非常快,但是由于其在系統被停止后所有的數據都會丟失,所以在通常應用中,都是使用 JobStoreSupport。

在 Quartz 中,JobStoreSupport 使用一個驅動代理來操作 trigger 和 job 的數據存儲:StdJDBCDelegate。StdJDBCDelegate 實現了大部分基于標準 JDBC 的功能接口,但是對于各種數據庫來說,需要根據其具體實現的特點做某些特殊處理,因此各種數據庫需要擴展 StdJDBCDelegate 以實現這些特殊處理。

在Hap中使用的就是JobStoreSupport持久化存儲,下面簡單介紹一下常見幾張表對應的關系:

image.png

這個界面的數據,存放在 qrtz_job_details 表中,但是這里還是有一些需要注意的地方。并不是所有的參數都在這個表中,比如 上次執行時間、下次執行時間、corn表達式等信息,實際上是存在別的表中,這個視 任務的類型來判斷。當簡單任務的時候,存在 qrtz_simple_triggers 表中;當 corn 任務的時候,存在 qrtz_triggers 表中。


image.png

不知道大家有沒有想過這個問題,任務中的參數是放在哪里?我一開始以為會放到一張表里,最后發現參數其實是存在 qrtz_job_details 表里的一個字段上的。

image.png

是一個blob類型。

其實Hap在Job這一塊封裝的東西不算是很多。我們在開發時主要用到的就是一個 AbstractJob 類,之后會介紹到。還有就是任務運行記錄,在Hap中是將任務執行記錄存在 sys_job_running_info 表中,其實也是在 quratz 的監聽器基礎上做到的,之后會有介紹。

配置

applicationContext-job.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- ~ #{copyright}# -->

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <context:property-placeholder location="classpath:config.properties"/>
    <bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- DO NOT AUTO STARTUP DURING LOCAL DEV TEST -->
        <property name="autoStartup" value="${job.autoStartup}" />
        <property name="jobFactory">
            <bean class="com.hand.hap.job.AutowiringSpringBeanJobFactory" />
        </property>
        <property name="applicationContextSchedulerContextKey" value="applicationContext" />
        <property name="configLocation" value="classpath:quartz.properties" />
    </bean>
</beans>

quartz.properties

#============================================================================
# Configure Scheduler
#============================================================================

org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.instanceId = AUTO
#org.quartz.scheduler.rmi.export = false
#org.quartz.scheduler.rmi.proxy = false
#org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
org.quartz.scheduler.skipUpdateCheck = true


#============================================================================
# Configure ThreadPool
#============================================================================
#org.quartz.threadPooln.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
#org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true


#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.maxMisfiresToHandleAtATime=1

#org.quartz.jobStore.misfireThreshold = 60000
#org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#org.quartz.scheduler.classLoadHelper.class=org.quartz.simpl.CascadingClassLoadHelper
#org.quartz.jobStore.useProperties = true

#============================================================================
# Microsoft SQL Server
#============================================================================
#org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.MSSQLDelegate
#org.quartz.jobStore.selectWithLockSQL=SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?




#============================================================================
# Configure Plugins 
#============================================================================
#org.quartz.plugin.triggHistory.class=org.quartz.plugins.history.LoggingJobHistoryPlugin
org.quartz.plugin.runningListener.class=com.hand.hap.job.plugin.RunningListenerPlugin
org.quartz.plugin.runningListener.LogRunningInfo=true
org.quartz.plugin.runningListener.mailTemplate=email_job_running_notification

quratz默認是使用RAMJobStore存儲,這是將信息存儲再內存中,這種方式有一個好處就是速度快,單缺點也很明顯:不能持久化存儲,如果系統出問題 就得從頭開始,所以在項目上我沒一般是采用持久化存儲這種方式。如果需要數據庫持久化存儲,一般需要在quartz.properties配置文件中將存儲方式改成jdbcjobstore,例如:

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate

但在Hap中并沒有直接在quartz.properties配置文件中定義數據庫得連接方式,而是在 applicationContext-job.xml 配置文件中將數據源注入進去了。

    <context:property-placeholder location="classpath:config.properties"/>
    <bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- DO NOT AUTO STARTUP DURING LOCAL DEV TEST -->
        <property name="autoStartup" value="${job.autoStartup}" />
        <property name="jobFactory">
            <bean class="com.hand.hap.job.AutowiringSpringBeanJobFactory" />
        </property>
        <property name="applicationContextSchedulerContextKey" value="applicationContext" />
        <property name="configLocation" value="classpath:quartz.properties" />
    </bean>

因為spring 不能在quartz中注入bean的,所以這里自定義了 一個 jobFactory

package com.hand.hap.job;

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;

/**
 * 支持 AutoWired.
 * 
 */
public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
    private transient AutowireCapableBeanFactory beanFactory;

    public void setApplicationContext(final ApplicationContext context) {
        beanFactory = context.getAutowireCapableBeanFactory();
    }

    @Override
    public Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
        final Object job = super.createJobInstance(bundle);
        beanFactory.autowireBean(job);
        return job;
    }
}
<property name="applicationContextSchedulerContextKey" value="applicationContext" />

applicationContextSchedulerContextKey 是 org.springframework.scheduling.quartz.SchedulerFactoryBean這個類中把spring上下 文以key/value的方式存放在了quartz的上下文中了,可以用applicationContextSchedulerContextKey所定義的key得到對應的spring上下文。

Listeners

這是Hap中Job模塊的關鍵部分。Listeners是您創建的對象,用于根據調度程序中發生的事件執行操作。

TriggerListeners和JobListeners

您可能猜到,TriggerListeners接收到與觸發器(trigger)相關的事件,JobListeners 接收與jobs相關的事件。
與觸發相關的事件包括:觸發器觸發,觸發失靈(在本文檔的“觸發器”部分中討論),并觸發完成(觸發器關閉的作業完成)。
org.quartz.TriggerListener接口

public interface TriggerListener {

    public String getName();

    public void triggerFired(Trigger trigger, JobExecutionContext context);

    public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context);

    public void triggerMisfired(Trigger trigger);

    public void triggerComplete(Trigger trigger, JobExecutionContext context,
            int triggerInstructionCode);
}

job相關事件包括:job即將執行的通知,以及job完成執行時的通知。
org.quartz.JobListener接口

public interface JobListener {

    public String getName();

    public void jobToBeExecuted(JobExecutionContext context);

    public void jobExecutionVetoed(JobExecutionContext context);

    public void jobWasExecuted(JobExecutionContext context,
            JobExecutionException jobException);

}

使用TriggerListeners和JobListeners

要創建一個listener,只需創建一個實現org.quartz.TriggerListener和/或org.quartz.JobListener接口的對象。然后,listener在運行時會向調度程序注冊,并且必須給出一個名稱(或者,他們必須通過他們的getName()方法來宣傳自己的名字)。
為了方便起見,實現這些接口,您的類也可以擴展JobListenerSupport類或TriggerListenerSupport類,并且只需覆蓋您感興趣的事件。
listener與調度程序的ListenerManager一起注冊,并配有描述listener希望接收事件的job/觸發器的Matcher。
Listener在運行時間內與調度程序一起注冊,并且不與jobs和觸發器一起存儲在JobStore中。這是因為聽眾通常是與應用程序的集成點。因此,每次運行應用程序時,都需要重新注冊該調度程序。

添加對特定job感興趣的JobListener

scheduler.getListenerManager().addJobListener(myJobListener,KeyMatcher.jobKeyEquals(new JobKey(“myJobName”,“myJobGroup”)));

這將上面的例子變成這樣:

scheduler.getListenerManager().addJobListener(myJobListener, jobKeyEquals(jobKey("myJobName", "myJobGroup")));

添加對特定組的所有job感興趣的JobListener:

scheduler.getListenerManager().addJobListener(myJobListener, jobGroupEquals("myJobGroup"));

添加對兩個特定組的所有job感興趣的JobListener:

scheduler.getListenerManager().addJobListener(myJobListener, or(jobGroupEquals("myJobGroup"), jobGroupEquals("yourGroup")));

添加對所有job感興趣的JobListener:

scheduler.getListenerManager().addJobListener(myJobListener, allJobs());

注冊TriggerListeners的工作原理相同。

Quartz的大多數用戶并不使用Listeners,但是當應用程序需求創建需要事件通知時不需要Job本身就必須明確地通知應用程序,這些用戶就很方便。

SchedulerListeners

SchedulerListeners非常類似于TriggerListeners和JobListeners,除了它們在Scheduler本身中接收到事件的通知 (不一定與特定觸發器(trigger)或job相關的事件)
與計劃程序相關的事件包括:添加job/觸發器,刪除job/觸發器,調度程序中的嚴重錯誤,關閉調度程序的通知等。

org.quartz.SchedulerListener接口:

public interface SchedulerListener {

    public void jobScheduled(Trigger trigger);

    public void jobUnscheduled(String triggerName, String triggerGroup);

    public void triggerFinalized(Trigger trigger);

    public void triggersPaused(String triggerName, String triggerGroup);

    public void triggersResumed(String triggerName, String triggerGroup);

    public void jobsPaused(String jobName, String jobGroup);

    public void jobsResumed(String jobName, String jobGroup);

    public void schedulerError(String msg, SchedulerException cause);

    public void schedulerStarted();

    public void schedulerInStandbyMode();

    public void schedulerShutdown();

    public void schedulingDataCleared();
}

SchedulerListeners注冊到調度程序的ListenerManager。SchedulerListeners幾乎可以實現任何實現org.quartz.SchedulerListener接口的對象。

添加SchedulerListener:

scheduler.getListenerManager().addSchedulerListener(mySchedListener);

刪除SchedulerListener:

scheduler.getListenerManager().removeSchedulerListener(mySchedListener);

Hap中的Listener

Hap中得Listener如下:

image.png

主要包括三類:JobListener、TriggerListener、SchedulerListener ;

前面那三個 Default開頭得Listener,沒有什么實質性得內容,只是分別單純得實現了 JobListener、TriggerListener、SchedulerListener 這個三個接口,這樣接下來得哪幾個Listener分別繼承Default開頭得那幾個Listener,就不用是實現JobListener、TriggerListener、SchedulerListener 接口中所有得方法了。這其實就是適配器模式中的 接口適配器模式。至于這幾個適配器里面的邏輯,就不過多說明了。可以簡單看看 SchedulerRunningListener:

/*
 * #{copyright}#
 */
package com.hand.hap.job.listener;

import com.hand.hap.job.service.IJobRunningInfoService;
import org.quartz.JobKey;
import org.quartz.listeners.SchedulerListenerSupport;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;

import com.hand.hap.job.dto.JobRunningInfoDto;

/**
 * @author shiliyan
 *
 */
public class SchedulerRunningListener extends SchedulerListenerSupport {

    private static final String JOB_INFO_HAS_DELETED = "Job Info [{}.{}] has deleted.";
    private static final String JOB_WAS_DELETED_FROM_SCHEDULER = "Job [{}.{}] was deleted from Scheduler.";
    private final ApplicationContext applicationContext;

    public SchedulerRunningListener(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    /*
     * (non-Javadoc)
     * 
     * @see DefaultSchedulerListener#jobDeleted(org.quartz.JobKey)
     */
    @Override
    public void jobDeleted(JobKey jobKey) {
        JobRunningInfoDto dto = new JobRunningInfoDto();
        String group = jobKey.getGroup();
        String name = jobKey.getName();
        logInfo(JOB_WAS_DELETED_FROM_SCHEDULER, group, name);
        dto.setJobName(name);
        dto.setJobGroup(group);
        deleteJobInfo(dto);
        logInfo(JOB_INFO_HAS_DELETED, group, name);
    }

    private void deleteJobInfo(JobRunningInfoDto jobCreateDto) {
        IJobRunningInfoService jobRunningInfoService = applicationContext.getBean(IJobRunningInfoService.class);
        jobRunningInfoService.delete(jobCreateDto);
    }

    protected void logInfo(String info, Object... para) {
        if (getLog().isInfoEnabled()) {
            getLog().info(info, para);
        }
    }

    protected void logInfo(String info) {
        if (getLog().isInfoEnabled()) {
            getLog().info(info);
        }
    }

}

有個 jobDeleted 方法,根據代碼可以看出,在刪除job的時候,會刪除 sys_job_running_info 表中對應Job的執行記錄。

插件SchedulerPlugin

在Hap 中的 quartz.properties 配置文件中,有這樣一行配置

org.quartz.plugin.runningListener.class=com.hand.hap.job.plugin.RunningListenerPlugin

這其實就是quartz的插件功能。
在上面我沒已經提到了Hap中對應Listener類型分別定義了幾個Listener,但是發現還少了點什么。并沒有在哪個地方看到JobRunningListener添加了需要監聽的Job。其實這些動作都在 RunningListenerPlugin 中。

image.png

DefaultSchedulerPlugin 實現了 SchedulerPlugin, 沒什么實質的內容

image.png

RunningListenerPlugin 繼承了 DefaultSchedulerPlugin,并重寫了 start 方法,然后將所有 Job和 JobRunningListener關聯起來

image.png

至此,有關于Hap中Job的內容到此結束。下一章節將介紹 quartz 在 soringboot2 中的應用。

整合Springboot

上一章節介紹了Quartz在Hap中的應用(其實就是在spring 中的應用)。本章節介紹 Quartz 在 springboot2中的應用。

添加依賴

        <!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz-jobs -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz-jobs</artifactId>
        </dependency>

有可莪能會報一個 c3p0的錯誤,這時候需要添加一個依賴

        <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

配置

///QuartzConfigure

package com.hand.sxy.config;

import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;

import javax.sql.DataSource;


/**
 * @author pavan.solapure
 */

@Configuration
@EnableScheduling
public class QuartzConfigure {


    /**
     * 繼承org.springframework.scheduling.quartz.SpringBeanJobFactory
     * 實現任務實例化方式
     */
    public static class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
            ApplicationContextAware {

        private transient AutowireCapableBeanFactory beanFactory;

        @Override
        public void setApplicationContext(final ApplicationContext context) {
            beanFactory = context.getAutowireCapableBeanFactory();
        }

        /**
         * 將job實例交給spring ioc托管
         * 我們在job實例實現類內可以直接使用spring注入的調用被spring ioc管理的實例
         *
         * @param bundle
         * @return
         * @throws Exception
         */
        @Override
        protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
            final Object job = super.createJobInstance(bundle);
            /**
             * 將job實例交付給spring ioc
             */
            beanFactory.autowireBean(job);
            return job;
        }
    }

    /**
     * 配置任務工廠實例
     *
     * @param applicationContext spring上下文實例
     * @return
     */
    @Bean
    public JobFactory jobFactory(ApplicationContext applicationContext) {
        /**
         * 采用自定義任務工廠 整合spring實例來完成構建任務
         * see {@link AutowiringSpringBeanJobFactory}
         */
        AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
        jobFactory.setApplicationContext(applicationContext);
        return jobFactory;
    }

    /**
     * 配置任務調度器
     * 使用項目數據源作為quartz數據源
     *
     * @param jobFactory 自定義配置任務工廠
     * @param dataSource 數據源實例
     * @return
     * @throws Exception
     */
    @Bean(destroyMethod = "destroy", autowire = Autowire.NO)
    public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory, DataSource dataSource) throws Exception {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        //將spring管理job自定義工廠交由調度器維護
        schedulerFactoryBean.setJobFactory(jobFactory);
        //設置覆蓋已存在的任務
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        //項目啟動完成后,等待2秒后開始執行調度器初始化
        schedulerFactoryBean.setStartupDelay(2);
        //設置調度器自動運行
        schedulerFactoryBean.setAutoStartup(true);
        //設置數據源,使用與項目統一數據源
        schedulerFactoryBean.setDataSource(dataSource);
        //設置上下文spring bean name
        schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContext");
        //設置配置文件位置
        schedulerFactoryBean.setConfigLocation(new ClassPathResource("/quartz.properties"));
        return schedulerFactoryBean;
    }
}

//quartz.properties

# 固定前綴org.quartz
# 主要分為scheduler、threadPool、jobStore、plugin等部分
#
#
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

# 實例化ThreadPool時,使用的線程類為SimpleThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

# threadCount和threadPriority將以setter的形式注入ThreadPool實例
# 并發個數
org.quartz.threadPool.threadCount = 5

# 優先級
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

org.quartz.jobStore.misfireThreshold = 5000

# 默認存儲在內存中
#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.StdJDBCDelegate
org.quartz.jobStore.useProperties = true 

org.quartz.jobStore.tablePrefix = QRTZ_

org.quartz.jobStore.dataSource = qzDS

org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver

org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/vue?characterEncoding=utf8&autoReconnect=true&useSSL=false

org.quartz.dataSource.qzDS.user = root

org.quartz.dataSource.qzDS.password = root

org.quartz.dataSource.qzDS.maxConnections = 10

使用

//BasalJob

package com.hand.sxy.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

/**
 * @author spilledyear
 */
public interface BasalJob extends Job {

    /**
     * Job執行入口
     *
     * @param context
     * @throws JobExecutionException
     */
    @Override
    void execute(JobExecutionContext context) throws JobExecutionException;
}

//HelloJob

package com.hand.sxy.job.example;

import com.hand.sxy.job.BasalJob;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;

/**
 * @author spilledyear
 */
public class HelloJob implements BasalJob {

    private static Logger logger = LoggerFactory.getLogger(HelloJob.class);

    public HelloJob() {

    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        logger.info("Hello Job執行時間: " + new Date());

    }
}  

//JobController

package com.hand.sxy.job.controller;

import com.hand.sxy.job.BasalJob;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;


/**
 * @author spilledyear
 */
@RestController
@RequestMapping(value = "/job")
public class JobController {
    private static Logger log = LoggerFactory.getLogger(JobController.class);

    @Autowired
    private Scheduler scheduler;


    @RequestMapping(value = "/addjob", method = RequestMethod.GET)
    public void addjob(@RequestParam(value = "jobGroup") String jobGroup,
                       @RequestParam(value = "jobName") String jobName,
                       @RequestParam(value = "jobClassName") String jobClassName,
                       @RequestParam(value = "cronExpression") String cronExpression) throws Exception {
        addJob(jobGroup, jobName, jobClassName, cronExpression);
    }

    public void addJob(String jobGroup, String jobName, String jobClassName, String cronExpression) throws Exception {

        // 啟動調度器
        scheduler.start();

        //構建job信息
        JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobGroup, jobName).build();

        //表達式調度構建器(即任務執行的時間)
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

        //按新的cronExpression表達式構建一個新的trigger
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobGroup, jobName)
                .withSchedule(scheduleBuilder).build();

        try {
            scheduler.scheduleJob(jobDetail, trigger);

        } catch (SchedulerException e) {
            System.out.println("創建定時任務失敗" + e);
            throw new Exception("創建定時任務失敗");
        }
    }
.....
}

在前端請求 /job/addJob接口(其實已經集成到了那兩個前后端分離的應用中,前端可視化管理Job)

后臺日志

image.png

數據庫數據

image.png

image.png

前端可視化管理


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容