34. 任务执行和调度

34.1 简介

Spring Framework分别使用 TaskExecutorTaskScheduler 接口提供异步执行和任务调度的抽象。 Spring还具有支持线程池或在应用程序服务器环境中委托给CommonJ的接口的实现。最终,在公共接口背后使用这些实现抽象出了Java SE 5,Java SE 6和Java EE环境之间的差异。

Spring还提供了集成类,用于支持使用 Timer 进行调度,这是自1.3以来JDK的一部分,以及Quartz Scheduler( http://quartz-scheduler.org )。这两个调度程序都使用 FactoryBean 进行设置,并分别对 TimerTrigger 实例进行可选引用。此外,Quartz Scheduler和 Timer 的便捷类可用,它允许您调用现有目标对象的方法(类似于正常的 MethodInvokingFactoryBean 操作)。

34.2 Spring TaskExecutor抽象

执行程序是线程池概念的JDK名称。 “执行程序”命名是由于无法保证底层实现实际上是一个池;执行程序可以是单线程甚至是同步的。 Spring的抽象隐藏了Java SE和Java EE环境之间的实现细节。

Spring的 TaskExecutor 接口与 java.util.concurrent.Executor 接口相同。实际上,最初,它存在的主要原因是在使用线程池时抽象出对Java 5的需求。该接口具有单个方法 execute(Runnable task) ,该方法根据线程池的语义和配置接受要执行的任务。

最初创建 TaskExecutor 是为了给其他Spring组件提供所需的线程池抽象。诸如 ApplicationEventMulticaster ,JMS的 AbstractMessageListenerContainer 和Quartz集成等组件都使用 TaskExecutor 抽象来池化线程。但是,如果您的bean需要线程池行为,则可以根据自己的需要使用此抽象。

34.2.1 TaskExecutor类型

Spring发行版中包含了许多预构建的 TaskExecutor 实现。很可能,你永远不需要实现自己的。常见的开箱即用变体是:

  • SyncTaskExecutor 此实现不会异步执行调用。相反,每次调用都发生在调用线程中。它主要用于不需要多线程的情况,例如简单的测试用例。

  • SimpleAsyncTaskExecutor 此实现不重用任何线程,而是为每次调用启动一个新线程。但是,它确实支持并发限制,该限制将阻止任何超出限制的调用,直到释放一个插槽。如果您正在寻找真正的汇集,请参阅下面的 ThreadPoolTaskExecutor

  • ConcurrentTaskExecutor 此实现是 java.util.concurrent.Executor 实例的适配器。有一个替代方法 ThreadPoolTaskExecutor ,它将 Executor 配置参数公开为bean属性。很少需要直接使用 ConcurrentTaskExecutor ,但如果 ThreadPoolTaskExecutor 不够灵活以满足您的需求,那么 ConcurrentTaskExecutor 是另一种选择。

  • ThreadPoolTaskExecutor 此实现是最常用的实现。它公开bean属性以配置 java.util.concurrent.ThreadPoolExecutor 并将其包装在 TaskExecutor 中。如果您需要适应不同类型的 java.util.concurrent.Executor ,建议您改用 ConcurrentTaskExecutor

  • WorkManagerTaskExecutor 此实现使用CommonJ WorkManager 作为其后备服务提供程序,并且是在Spring应用程序上下文中在WebLogic / WebSphere上设置基于CommonJ的线程池集成的中心便利类。

  • DefaultManagedTaskExecutor 此实现在JSR-236兼容的运行时环境(例如Java EE 7应用程序服务器)中使用JNDI获取的 ManagedExecutorService ,为此目的替换CommonJ WorkManager。

34.2.2 使用TaskExecutor

Spring的 TaskExecutor 实现用作简单的JavaBeans。在下面的示例中,我们定义了一个使用 ThreadPoolTaskExecutor 来异步打印一组消息的bean。

import org.springframework.core.task.TaskExecutor;

public class TaskExecutorExample {

    private class MessagePrinterTask implements Runnable {

        private String message;

        public MessagePrinterTask(String message) {
            this.message = message;
        }

        public void run() {
            System.out.println(message);
        }
    }

    private TaskExecutor taskExecutor;

    public TaskExecutorExample(TaskExecutor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

    public void printMessages() {
        for(int i = 0; i < 25; i++) {
            taskExecutor.execute(new MessagePrinterTask("Message" + i));
        }
    }
}

如您所见,您不是从池中检索线程并自行执行,而是将 Runnable 添加到队列中, TaskExecutor 使用其内部规则来决定何时执行任务。

要配置 TaskExecutor 将使用的规则,已公开简单的bean属性。

<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="5"/>
    <property name="maxPoolSize" value="10"/>
    <property name="queueCapacity" value="25"/>
</bean>

<bean id="taskExecutorExample" class="TaskExecutorExample">
    <constructor-arg ref="taskExecutor"/>
</bean>

34.3 Spring TaskScheduler抽象

除了 TaskExecutor 抽象之外,Spring 3.0引入了一个 TaskScheduler ,其中包含各种方法,用于在将来的某个时间点调度任务。

public interface TaskScheduler {

    ScheduledFuture schedule(Runnable task, Trigger trigger);

    ScheduledFuture schedule(Runnable task, Date startTime);

    ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);

    ScheduledFuture scheduleAtFixedRate(Runnable task, long period);

    ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);

    ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);
}

最简单的方法是名为'schedule'的方法,仅采用 RunnableDate 。这将导致任务在指定时间后运行一次。所有其他方法都能够安排任务重复运行。固定速率和固定延迟方法用于简单的定期执行,但接受触发的方法更灵活。

34.3.1 触发界面

Trigger 接口基本上受到JSR-236的启发,从Spring 3.0开始,它还没有正式实现。 Trigger 的基本思想是执行时间可以根据过去的执行结果甚至任意条件来确定。如果这些确定确实考虑了前面执行的结果,则该信息在 TriggerContext 中可用。 Trigger 界面本身很简单:

public interface Trigger {

    Date nextExecutionTime(TriggerContext triggerContext);
}

如您所见, TriggerContext 是最重要的部分。它封装了所有相关数据,如有必要,将来可以进行扩展。 TriggerContext 是一个接口(默认使用 SimpleTriggerContext 实现)。在这里,您可以看到 Trigger 实现可用的方法。

public interface TriggerContext {

    Date lastScheduledExecutionTime();

    Date lastActualExecutionTime();

    Date lastCompletionTime();
}

34.3.2 触发器实现

Spring提供了 Trigger 接口的两个实现。最有趣的是 CronTrigger 。它支持基于cron表达式调度任务。例如,以下任务计划在每小时过去15分钟后运行,但仅在工作日的9到5 "business hours" 期间运行。

scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));

另一个开箱即用的实现是 PeriodicTrigger ,它接受固定周期,可选的初始延迟值和布尔值,以指示周期是应该被解释为固定速率还是固定延迟。由于 TaskScheduler 接口已经定义了以固定速率或固定延迟来调度任务的方法,因此应尽可能直接使用这些方法。 PeriodicTrigger 实现的值是它可以在依赖于 Trigger 抽象的组件中使用。例如,允许定期触发,基于cron的触发器甚至自定义触发器实现可以互换使用可能是方便的。这样的组件可以利用依赖注入,以便可以在外部配置这样的组件,因此可以容易地修改或扩展。

34.3.3 TaskScheduler实现

与Spring的 TaskExecutor 抽象一样, TaskScheduler 安排的主要好处是应用程序的调度需求与部署环境分离。在部署到不应由应用程序本身直接创建线程的应用程序服务器环境时,此抽象级别尤其重要。对于这样的场景,Spring提供了一个 TimerManagerTaskScheduler 委托给WebLogic / WebSphere上的CommonJ TimerManager以及一个更新的 DefaultManagedTaskScheduler 委托给Java EE 7环境中的JSR-236 ManagedScheduledExecutorService ,两者通常都配置了JNDI查找。

每当外部线程管理不是必需的时候,更简单的替代方案是应用程序中的本地 ScheduledExecutorService 设置,可以通过Spring的 ConcurrentTaskScheduler 进行调整。为方便起见,Spring还提供了 ThreadPoolTaskScheduler ,它在内部委托给 ScheduledExecutorService ,提供了 ThreadPoolTaskExecutor 行的常见bean样式配置。这些变体适用于宽松应用程序服务器环境中的本地嵌入式线程池设置,特别是在Tomcat和Jetty上。

34.4 对调度和异步执行的注释支持

Spring为任务调度和异步方法执行提供注释支持。

34.4.1 启用调度注释

要启用对 @Scheduled@Async 注释的支持,请将 @EnableScheduling@EnableAsync 添加到您的 @Configuration 类之一:

@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}

您可以自由选择适合您应用的相关注释。例如,如果您只需要支持 @Scheduled ,则只需省略 @EnableAsync 。对于更细粒度的控制,您还可以实现 SchedulingConfigurer 和/或 AsyncConfigurer 接口。有关详细信息,请参阅javadocs。

如果您更喜欢XML配置,请使用 <task:annotation-driven> 元素。

<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>

请注意,上面的XML提供了一个执行程序引用,用于处理与具有 @Async 注释的方法相对应的那些任务,并提供了调度程序引用来管理那些使用 @Scheduled 注释的方法。

处理@Async注释的默认建议模式是“proxy”,它允许仅通过代理拦截调用;同一类中的本地调用不能以这种方式截获。对于更高级的拦截模式,请考虑结合编译时或加载时编织切换到“aspectj”模式。

34.4.2 @Scheduled注释

可以将 @Scheduled 注释与触发器元数据一起添加到方法中。例如,以固定延迟每5秒调用以下方法,这意味着将从每个前一次调用的完成时间开始测量该周期。

@Scheduled(fixedDelay=5000)
public void doSomething() {
    // something that should execute periodically
}

如果需要固定费率执行,只需更改注释中指定的属性名称即可。在每次调用的连续开始时间之间测量的每5秒执行以下操作。

@Scheduled(fixedRate=5000)
public void doSomething() {
    // something that should execute periodically
}

对于固定延迟和固定速率任务,可以指定初始延迟,指示在第一次执行该方法之前等待的毫秒数。

@Scheduled(initialDelay=1000, fixedRate=5000)
public void doSomething() {
    // something that should execute periodically
}

如果简单的周期性调度不够表达,则可以提供cron表达式。例如,以下内容仅在工作日执行。

@Scheduled(cron="*/5 * * * * MON-FRI")
public void doSomething() {
    // something that should execute on weekdays only
}

您还可以使用zone属性指定解析cron表达式的时区。

请注意,要调度的方法必须具有void返回值并且不能指望任何争论。如果该方法需要与Application Context中的其他对象进行交互,则通常会通过依赖注入提供这些对象。

从Spring Framework 4.3开始,任何范围的bean都支持@Scheduled方法。确保您没有在运行时初始化同一个@Scheduled注释类的多个实例,除非您确实要为每个此类实例安排回调。与此相关,请确保不要对使用@Scheduled注释并使用容器注册为常规Spring bean的bean类使用@Configurable:否则将获得双初始化,一次通过容器,一次通过@Configurable方面,每个@Scheduled方法的结果被调用两次。

34.4.3 @Async注释

可以在方法上提供 @Async 注释,以便异步调用该方法。换句话说,调用者将在调用时立即返回,并且该方法的实际执行将发生在已提交给Spring TaskExecutor 的任务中。在最简单的情况下,注释可以应用于 void -返回方法。

@Async
void doSomething() {
    // this will be executed asynchronously
}

与使用 @Scheduled 注释注释的方法不同,这些方法可以使用参数,因为它们将在运行时由调用者以 "normal" 方式调用,而不是由容器管理的调度任务调用。例如,以下是 @Async 注释的合法应用程序。

@Async
void doSomething(String s) {
    // this will be executed asynchronously
}

甚至可以异步调用返回值的方法。但是,此类方法需要具有 Future 类型的返回值。这仍然提供了异步执行的好处,以便调用者可以在调用Future上的 get() 之前执行其他任务。

@Async
Future<String> returnSomething(int i) {
    // this will be executed asynchronously
}

@Async方法不仅可以声明一个常规的java.util.concurrent.Future返回类型,还可以声明Spring的org.springframework.util.concurrent.ListenableFuture,或者从Spring 4.2开始,JDK 8的java.util.concurrent.CompletableFuture:for与异步任务更丰富的交互以及通过进一步处理步骤立即组合。

@Async 不能与生命周期回调一起使用,例如 @PostConstruct 。要异步初始化Spring bean,您当前必须使用单独的初始化Spring bean,然后在目标上调用 @Async 带注释的方法。

public class SampleBeanImpl implements SampleBean {

    @Async
    void doSomething() {
        // ...
    }

}

public class SampleBeanInitializer {

    private final SampleBean bean;

    public SampleBeanInitializer(SampleBean bean) {
        this.bean = bean;
    }

    @PostConstruct
    public void initialize() {
        bean.doSomething();
    }

}

@Async没有直接的XML等价物,因为这些方法应首先设计用于异步执行,而不是外部重新声明为异步。但是,您可以使用Spring AOP手动设置Spring的AsyncExecutionInterceptor,并结合自定义切入点。

34.4.4 使用@Async执行者资格

默认情况下,在方法上指定 @Async 时,将使用的执行程序是提供给“注释驱动”元素的执行程序,如上所述。但是,当需要指示在执行给定方法时应使用非默认执行程序时,可以使用 @Async 注释的 value 属性。

@Async("otherExecutor")
void doSomething(String s) {
    // this will be executed asynchronously by "otherExecutor"
}

在这种情况下, "otherExecutor" 可以是Spring容器中任何 Executor bean的名称,也可以是与任何 Executor 关联的限定符的名称,例如,与 <qualifier> 元素或Spring的 @Qualifier 注释一起指定。

34.4.5 使用@Async进行异常管理

@Async 方法具有 Future 类型的返回值时,很容易管理在方法执行期间抛出的异常,因为在 Future 结果上调用 get 时将抛出此异常。但是,对于void返回类型,异常未被捕获且无法传输。对于这些情况,可以提供 AsyncUncaughtExceptionHandler 来处理此类异常。

public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        // handle exception
    }
}

默认情况下,只记录异常。可以通过 AsyncConfigurertask:annotation-driven XML元素定义自定义 AsyncUncaughtExceptionHandler

34.5 任务名称空间

从Spring 3.0开始,有一个用于配置 TaskExecutorTaskScheduler 实例的XML命名空间。它还提供了一种方便的方法来配置要使用触发器安排的任务。

34.5.1 'scheduler'元素

以下元素将创建具有指定线程池大小的 ThreadPoolTaskScheduler 实例。

<task:scheduler id="scheduler" pool-size="10"/>

为'id'属性提供的值将用作池中线程名称的前缀。 'scheduler'元素相对简单。如果未提供“pool-size”属性,则默认线程池将只有一个线程。调度程序没有其他配置选项。

34.5.2 'executor'元素

以下将创建一个 ThreadPoolTaskExecutor 实例:

<task:executor id="executor" pool-size="10"/>

与上面的调度程序一样,为'id'属性提供的值将用作池中线程名称的前缀。就池大小而言,'executor'元素支持比'scheduler'元素更多的配置选项。首先, ThreadPoolTaskExecutor 的线程池本身更易于配置。执行程序的线程池可能具有不同的核心值和最大大小,而不仅仅是单个大小。如果提供单个值,则执行程序将具有固定大小的线程池(核心和最大大小相同)。但是,'executor'元素的'pool-size'属性也接受 "min-max" 形式的范围。

<task:executor
        id="executorWithPoolSizeRange"
        pool-size="5-25"
        queue-capacity="100"/>

从该配置中可以看出,还提供了“队列容量”值。还应根据执行程序的队列容量来考虑线程池的配置。有关池大小和队列容量之间关系的完整描述,请参阅 ThreadPoolExecutor 的文档。主要思想是,当提交任务时,如果活动线程的数量当前小于核心大小,则执行程序将首先尝试使用空闲线程。如果已达到核心大小,则只要尚未达到其容量,任务将添加到队列中。只有这样,如果已达到队列的容量,执行程序是否会创建超出核心大小的新线程。如果还达到了最大大小,则执行程序将拒绝该任务。

默认情况下,队列是无限制的,但这很少是所需的配置,因为如果在所有池线程忙的情况下将足够的任务添加到该队列,则可能导致 OutOfMemoryErrors 。此外,如果队列是无界的,那么最大大小根本没有影响。由于执行程序将始终在创建超出核心大小的新线程之前尝试队列,因此队列必须具有有限的容量,以使线程池增长超出核心大小(这就是为什么固定大小的池是使用时唯一合理的情况一个无限的队列)。

稍后,我们将回顾保持活动设置的效果,这增加了在提供池大小配置时要考虑的另一个因素。首先,如上所述,让我们考虑一个任务被拒绝的情况。默认情况下,当任务被拒绝时,线程池执行程序将抛出 TaskRejectedException 。但是,拒绝策略实际上是可配置的。使用默认拒绝策略( AbortPolicy 实现)时会抛出异常。对于可以在高负载下跳过某些任务的应用程序,可以配置 DiscardPolicyDiscardOldestPolicy 。另一个适用于需要在高负载下限制提交的任务的应用程序的选项是 CallerRunsPolicy 。该策略不会抛出异常或丢弃任务,而只是强制调用submit方法的线程自己运行任务。这个想法是这样的调用者在运行该任务时会很忙,并且不能立即提交其他任务。因此,它提供了一种简单的方法来限制传入的负载,同时保持线程池和队列的限制。通常,这允许执行程序对其正在处理的任务执行 "catch up" ,从而释放队列,池中或两者中的某些容量。可以从'executor'元素上'rejection-policy'属性的可用值枚举中选择任何这些选项。

<task:executor
        id="executorWithCallerRunsPolicy"
        pool-size="5-25"
        queue-capacity="100"
        rejection-policy="CALLER_RUNS"/>

最后, keep-alive 设置确定线程在终止之前可以保持空闲的时间限制(以秒为单位)。如果池中当前有多个线程核心数,则在等待这段时间而不处理任务后,多余的线程将被终止。时间值为零将导致多余线程在执行任务后立即终止,而不会在任务队列中保留后续工作。

<task:executor
        id="executorWithKeepAlive"
        pool-size="5-25"
        keep-alive="120"/>

34.5.3 'scheduled-tasks'元素

Spring的任务命名空间最强大的功能是支持配置要在Spring应用程序上下文中调度的任务。这遵循类似于Spring中的其他“方法调用者”的方法,例如由JMS名称空间提供的用于配置消息驱动的POJO的方法。基本上,“ref”属性可以指向任何Spring管理的对象,“method”属性提供要在该对象上调用的方法的名称。这是一个简单的例子。

<task:scheduled-tasks scheduler="myScheduler">
    <task:scheduled ref="beanA" method="methodA" fixed-delay="5000"/>
</task:scheduled-tasks>

<task:scheduler id="myScheduler" pool-size="10"/>

如您所见,调度程序由外部元素引用,每个单独的任务都包含其触发器元数据的配置。在前面的示例中,该元数据定义了具有固定延迟的周期性触发,该延迟指示在每个任务执行完成之后等待的毫秒数。另一种选择是“固定速率”,表示无论先前执行多长时间,该方法应执行的频率。另外,对于固定延迟和固定速率任务,可以指定“初始延迟”参数,该参数指示在第一次执行该方法之前等待的毫秒数。为了更多控制,可以提供“cron”属性。这是一个展示这些的例子其他选择。

<task:scheduled-tasks scheduler="myScheduler">
    <task:scheduled ref="beanA" method="methodA" fixed-delay="5000" initial-delay="1000"/>
    <task:scheduled ref="beanB" method="methodB" fixed-rate="5000"/>
    <task:scheduled ref="beanC" method="methodC" cron="*/5 * * * * MON-FRI"/>
</task:scheduled-tasks>

<task:scheduler id="myScheduler" pool-size="10"/>

34.6 使用Quartz Scheduler

Quartz使用 TriggerJobJobDetail 对象来实现各种作业的调度。有关Quartz背后的基本概念,请查看 http://quartz-scheduler.org 。为方便起见,Spring提供了几个类,简化了基于Spring的应用程序中Quartz的使用。

34.6.1 使用JobDetailFactoryBean

Quartz JobDetail 对象包含运行作业所需的所有信息。 Spring提供了 JobDetailFactoryBean ,它为XML配置提供了bean风格的属性。我们来看一个例子:

<bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="example.ExampleJob"/>
    <property name="jobDataAsMap">
        <map>
            <entry key="timeout" value="5"/>
        </map>
    </property>
</bean>

作业详细信息配置包含运行作业所需的所有信息( ExampleJob )。超时在作业数据映射中指定。作业数据映射可通过 JobExecutionContext (在执行时传递给您)获得,但 JobDetail 也从映射到作业实例属性的作业数据中获取其属性。所以在这种情况下,如果 ExampleJob 包含名为 timeout 的bean属性,则 JobDetail 将自动应用它:

package example;

public class ExampleJob extends QuartzJobBean {

    private int timeout;

    /**
     * Setter called after the ExampleJob is instantiated
     * with the value from the JobDetailFactoryBean (5)
     */
    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
        // do the actual work
    }

}

当然,您也可以使用作业数据图中的所有其他属性。

使用名称和组属性,可以分别修改作业的名称和组。默认情况下,作业的名称与JobDetailFactoryBean的bean名称匹配(在上面的示例中,这是exampleJob)。

34.6.2 使用MethodInvokingJobDetailFactoryBean

通常,您只需要在特定对象上调用方法。使用 MethodInvokingJobDetailFactoryBean 你可以这样做:

<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject" ref="exampleBusinessObject"/>
    <property name="targetMethod" value="doIt"/>
</bean>

上面的例子将导致在 exampleBusinessObject 方法上调用 doIt 方法(见下文):

public class ExampleBusinessObject {

    // properties and collaborators

    public void doIt() {
        // do the actual work
    }
}
<bean id="exampleBusinessObject" class="examples.ExampleBusinessObject"/>

使用 MethodInvokingJobDetailFactoryBean ,您不需要创建仅调用方法的单行作业,而只需创建实际的业务对象并连接详细信息对象。

默认情况下,Quartz Jobs是无状态的,导致作业相互干扰的可能性。如果为同一个 JobDetail 指定两个触发器,则可能在第一个作业完成之前,第二个作业将启动。如果 JobDetail 类实现 Stateful 接口,则不会发生这种情况。第二个作业在第一个作业完成之前不会开始。要使 MethodInvokingJobDetailFactoryBean 非并发产生作业,请将 concurrent 标志设置为 false

<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject" ref="exampleBusinessObject"/>
    <property name="targetMethod" value="doIt"/>
    <property name="concurrent" value="false"/>
</bean>

默认情况下,作业将以并发方式运行。

34.6.3 使用触发器和SchedulerFactoryBean连接作业

我们已经创建了工作细节和工作。我们还回顾了允许您在特定对象上调用方法的便捷bean。当然,我们仍然需要自己安排工作。这是使用触发器和 SchedulerFactoryBean 完成的。 Quartz中提供了几个触发器,Spring提供了两个带有方便默认值的Quartz FactoryBean 实现: CronTriggerFactoryBeanSimpleTriggerFactoryBean

需要安排触发器。 Spring提供 SchedulerFactoryBean ,公开要设置为属性的触发器。 SchedulerFactoryBean 使用这些触发器安排实际作业。

以下是几个例子:

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
    <!-- see the example of method invoking job above -->
    <property name="jobDetail" ref="jobDetail"/>
    <!-- 10 seconds -->
    <property name="startDelay" value="10000"/>
    <!-- repeat every 50 seconds -->
    <property name="repeatInterval" value="50000"/>
</bean>

<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="jobDetail" ref="exampleJob"/>
    <!-- run every morning at 6 AM -->
    <property name="cronExpression" value="0 0 6 * * ?"/>
</bean>

现在我们设置了两个触发器,一个每50秒运行一次,启动延迟为10秒,每天早上6点运行一次。要完成所有事情,我们需要设置 SchedulerFactoryBean

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="cronTrigger"/>
            <ref bean="simpleTrigger"/>
        </list>
    </property>
</bean>

您可以为 SchedulerFactoryBean 设置更多属性,例如作业详细信息使用的日历,自定义Quartz的属性等。有关详细信息,请查看 SchedulerFactoryBean javadocs

Updated at: 5 months ago
基于速度的例子Table of content35. 动态语言支持
Comment
You are not logged in.

There are no comments.