最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 正文

spring定时任务

来源:动视网 责编:小OO 时间:2025-10-01 01:58:10
文档

spring定时任务

Chapter23.Spring中的定时调度(Scheduling)和线程池(ThreadPooling)23.1.简介Spring包含了对定时调度服务的内置支持类。当前,Spring支持从JDK1.3开始内置的Timer类和QuartzScheduler(http://www.opensymphony.com/quartz/)。二者都可以通过FactoryBean,分别指向Timer或Trigger实例的引用进行配置。更进一步,有个对QuartzScheduler和Timer都有效的工具类可
推荐度:
导读Chapter23.Spring中的定时调度(Scheduling)和线程池(ThreadPooling)23.1.简介Spring包含了对定时调度服务的内置支持类。当前,Spring支持从JDK1.3开始内置的Timer类和QuartzScheduler(http://www.opensymphony.com/quartz/)。二者都可以通过FactoryBean,分别指向Timer或Trigger实例的引用进行配置。更进一步,有个对QuartzScheduler和Timer都有效的工具类可
Chapter 23. Spring中的定时调度(Scheduling)和线程池(Thread Pooling)

23.1. 简介

Spring包含了对定时调度服务的内置支持类。当前,Spring支持从JDK1.3开始内置的Timer类和Quartz Scheduler(http://www.opensymphony.com/quartz/)。二者都可以通过FactoryBean,分别指向Timer或Trigger实例的引用进行配置。更进一步,有个对Quartz Scheduler和Timer都有效的工具类可以让你调用某个目标对象的方法(类似通常的MethodInvokingFactoryBean操作)。Spring 还包含有用于线程池调度的类,它针对Java 1.3,1.4,5和JEE环境的差异都进行了抽象。 

23.2. 使用OpenSymphony Quartz 调度器

Quartz使用Trigger, Job以及JobDetail等对象来进行各种类型的任务调度。关于Quartz的基本概念,请参阅http://www.opensymphony.com/quartz。为了让基于Spring的应用程序方便使用,Spring提供了一些类来简化uartz的用法。 

23.2.1. 使用JobDetailBean

JobDetail 对象保存运行一个任务所需的全部信息。Spring提供一个叫作JobDetailBean的类让JobDetail能对一些有意义的初始值进行初始化。让我们来看个例子: 

  

  

    

      

    

  

Job detail bean拥有所有运行job(ExampleJob)的必要信息。通过job的data map来制定timeout。Job的data map可以通过JobExecutionContext(在运行时刻传递给你)来得到,但是JobDetailBean同时把从job的data map中得到的属性映射到实际job中的属性中去。 所以,如果ExampleJob中包含一个名为timeout的属性,JobDetailBean将自动为它赋值: 

package example;

public class ExampleJob extends QuartzJobBean {

   private int timeout;

   

   /**

    * Setter called after the ExampleJob is instantiated

    * with the value from the JobDetailBean (5)

    */ 

   public void setTimeout(int timeout) {

     this.timeout = timeout;

   }

   

   protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {

      // do the actual work

   }

}

当然,你同样可以对Job detail bean中所有其他的额外配置进行设置。 

注意:使用name和group属性,你可以分别修改job在哪一个组下运行和使用什么名称。 默认情况下,job的名称等于job detail bean的名称(在上面的例子中为exampleJob)。 

23.2.2. 使用 MethodInvokingJobDetailFactoryBean

通常情况下,你只需要调用特定对象上的一个方法即可实现任务调度。你可以使用MethodInvokingJobDetailFactoryBean准确的做到这一点: 

  

  

上面例子将调用exampleBusinessObject中的doIt方法(如下): 

public class ExampleBusinessObject {

   

  // properties and collaborators

   

   public void doIt() {

    // do the actual work

   }

}

使用MethodInvokingJobDetailFactoryBean你不需要创建只有一行代码且只调用一个方法的job, 你只需要创建真实的业务对象来包装具体的细节的对象。 

默认情况下,Quartz Jobs是无状态的,可能导致jobs之间互相的影响。如果你为相同的JobDetail指定两个Trigger, 很可能当第一个job完成之前,第二个job就开始了。如果JobDetail对象实现了Stateful接口,就不会发生这样的事情。 第二个job将不会在第一个job完成之前开始。为了使得jobs不并发运行,设置MethodInvokingJobDetailFactoryBean中的concurrent标记为false。 

  

  

  

Note
注意:默认情况下,jobs在并行的方式下运行。 

23.2.3. 使用triggers和SchedulerFactoryBean来包装任务

我们已经创建了job details,jobs。我们同时回顾了允许你调用特定对象上某一个方法的便捷的bean。 当然我们仍需要调度这些jobs。这需要使用triggers和SchedulerFactoryBean来完成。 Quartz自带一些可供使用的triggers。Spring提供两个子类triggers,分别为CronTriggerBean和SimpleTriggerBean。 

Triggers也需要被调度。Spring提供SchedulerFactoryBean来暴露一些属性来设置triggers。SchedulerFactoryBean负责调度那些实际的triggers。 

几个例子: 

    

    

    

    

    

    

    

    

    

现在我们创建了两个triggers,其中一个开始延迟10秒以后每50秒运行一次,另一个每天早上6点钟运行。 我们需要创建一个SchedulerFactoryBean来最终实现上述的一切: 

    

        

            

            

        

    

更多的属性你可以通过SchedulerFactoryBean来设置,例如job details使用的Calendars, 用来订制Quartz的一些属性以及其它相关信息。 你可以查阅相应的JavaDOC(http://www.springframework.org/docs/api/org/springframework/scheduling/quartz/SchedulerFactoryBean.html)来了解进一步的信息。 

23.3. 使用JDK Timer支持类

另外一个调度任务的途径是使用JDK Timer对象。更多的关于Timer的信息可以在这里http://java.sun.com/docs/books/tutorial/essential/threads/timer.html找到。 上面讨论的概念仍可以应用于Timer的支持。你可以创建定制的timer或者调用某些方法的timer。包装timers的工作由TimerFactoryBean完成。 

23.3.1. 创建定制的timers

你可以使用TimerTask创建定制的timer tasks,类似于Quartz中的jobs: 

public class CheckEmailAddresses extends TimerTask {

   private List emailAddresses;

   

   public void setEmailAddresses(List emailAddresses) {

     this.emailAddresses = emailAddresses;

   }

   

   public void run() {

    // iterate over all email addresses and archive them

   }

}

包装它很简单: 

    

        

             test@springframework.org

             foo@bar.com

             john@doe.net

        

    

    

     

    

     

    

注意若要让任务只运行一次,你可以把period属性设置为0(或者负值)。 

23.3.2. 使用 MethodInvokingTimerTaskFactoryBean类

和对Quartz的支持类似,对Timer的支持也包含一个组件,可以让你周期性的调用某个方法: 

    

    

以上的例子会调用exampleBusinessObject对象的doIt方法。(见下): 

public class BusinessObject {

   

  // properties and collaborators

   

   public void doIt() {

    // do the actual work

   }

}

将上例中ScheduledTimerTask的timerTask引用修改为doIt,bean将会用一个固定的周期来调用doIt方法。 

23.3.3. 打包:使用TimerFactoryBean来设置任务

TimerFactoryBean类和Quartz的SchedulerFactoryBean类有些类似,它们是为同样的目的而设计的:设置确切的任务计划。TimerFactoryBean对一个Timer进行配置,设置其引用的任务的周期。你可以指定是否使用背景线程。 

    

        

            

             

        

    

23.4. SpringTaskExecutor抽象

Spring 2.0 为执行器(Executor)处理引入了一个新的抽象层。Executor是Java 5的名词,用来表示线程池的概念。之所以用这个奇怪的名词,是因为实际上不能保证底层实现的确是一个池。实际上,很多情况下,executor只是单线程。Spring的抽象层帮助你把线程池引入到Java 1.3和1.4环境中,同时隐藏了 1.3, 1.4, 5, 和 Java EE环境中线程池实现的差异。 

23.4.1. TaskExecutor接口

Spring的TaskExecutor接口等同于java.util.concurrent.Executor接口。实际上,它存在主要原因是为了在使用线程池的时候,将对Java 5的依赖抽象掉。这个接口只有一个方法execute(Runnable task),它根据线程池的语义和配置,来接受一个执行任务。 

23.4.2. 何时使用TaskExecutor接口

TaskExecutor接口开始的时候,是为了其他Spring组件使用线程池的抽象需要创建的。例如ApplicationEventMulticaster组件、JMS的 AbstractMessageListenerContainer和对Quartz的整合都使用了TaskExecutor接口来抽象线程池。当然,如果你的bean需要线程池行为,你也可以使用这个抽象层。 

23.4.3. TaskExecutor类型

在Spring发行包中预定义了一些TaskExecutor实现。有了它们,你甚至不需要再自行实现了。 

∙SimpleAsyncTaskExecutor类 

这个实现不重用任何线程,或者说它每次调用都启动一个新线程。但是,它还是支持对并发总数设限,当超过线程并发总数时,阻塞新的调用,直到有位置被释放。如果你需要真正的池,请继续往下看。 

∙SyncTaskExecutor类 

这个实现不会异步执行。相反,每次调用都在发起调用的线程中执行。它的主要用处是在不需要多线程的时候,比如简单的test case。 

∙ConcurrentTaskExecutor类 

这个实现是对Java 5 java.util.concurrent.Executor类的包装。有另一个备选, ThreadPoolTaskExecutor类,它暴露了Executor的配置参数作为bean属性。很少需要使用ConcurrentTaskExecutor, 但是如果ThreadPoolTaskExecutor不敷所需,ConcurrentTaskExecutor是另外一个备选。 

∙SimpleThreadPoolTaskExecutor类 

这个实现实际上是Quartz的SimpleThreadPool类的子类,它会监听Spring的生命周期回调。当你有线程池,需要在Quartz和非Quartz组件用时,这是它的典型用处。 

∙ThreadPoolTaskExecutor类 

它不支持任何对java.util.concurrent包的替换或者下行移植。Doug Lea和Dawid Kurzyniec对java.util.concurrent的实现都采用了不同的包结构,导致它们无法正确运行。 

这个实现只能在Java 5环境中使用,但是却是这个环境中最常用的。它暴露的bean properties可以用来配置一个java.util.concurrent.ThreadPoolExecutor,把它包装到一个TaskExecutor中。如果你需要更加先进的类,比如ScheduledThreadPoolExecutor,我们建议你使用ConcurrentTaskExecutor来替代。 

∙TimerTaskExecutor类 

这个实现使用一个TimerTask作为其背后的实现。它和SyncTaskExecutor的不同在于,方法调用是在一个的线程中进行的,虽然在那个线程中是同步的。 

∙WorkManagerTaskExecutor类 

CommonJ 是BEA和IBM联合开发的一套规范。这些规范并非Java EE的标准,但它是BEA和IBM的应用服务器实现的共同标准 

这个实现使用了CommonJ WorkManager作为其底层实现,是在Spring context中配置CommonJ WorkManager应用的最重要的类。和SimpleThreadPoolTaskExecutor类似,这个类实现了WorkManager接口,因此可以直接作为WorkManager使用。 

23.4.4. 使用TaskExecutor接口

Spring的TaskExecutor实现作为一个简单的JavaBeans使用。在下面的示例中,我们定义一个bean,使用 ThreadPoolTaskExecutor来异步打印出一系列字符串。 

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 properties。 

  

  

  

  

2008-11-14

spring设置动态定时任务

关键字: spring设置动态定时任务 

 

Quartz 在 Spring 中动态设置 cronExpression 

什么是动态定时任务:是由客户制定生成的,服务端只知道该去执行什么任务,但任务的定时是不确定的(是由客户制定)。 

这样总不能修改配置文件每定制个定时任务就增加一个trigger 吧,即便允许客户修改配置文件,但总需要重新启动web 服务啊,研究了下Quartz 在Spring 中的动态定时,发现 

         

         

              0/10 * * * * ?

         

      中cronExpression 是关键,如果可以动态设置cronExpression 的值,也就说如果我们可以直接调用CronTriggerBean 中设置cronExpression 的方法,就可以顺利解决问题了。 

熟悉1 的朋友可以跳过不看,下面2 、3 是动态定时任务的具体实现。 

1 . Quartz 在Spring 中的简单配置 

Spring 配置文件: 

    

        

          

        

    

    

         

           

              0/10 * * * * ?

         

     

    

        

            

                

            

        

在上面的配置中设定了 

① targetMethod: 指定需要定时执行scheduleInfoAction 中的simpleJobTest() 方法 

② concurrent :对于相同的JobDetail ,当指定多个Trigger 时, 很可能第一个job 完成之前,第二个job 就开始了。指定concurrent 设为false ,多个job 不会并发运行,第二个job 将不会在第一个job 完成之前开始。 

③ cronExpression :0/10 * * * * ? 表示每10 秒执行一次,具体可参考附表 。 

④ triggers :通过再添加其他的ref 元素可在list 中放置多个触发器。 

scheduleInfoAction 中的simpleJobTest() 方法 

注意:此方法没有参数,如果scheduleInfoAction 有两个方法simpleJobTest () 和simpleJobTest(String argument) ,则spring 只会去执行无参的simpleJobTest (). 

public void simpleJobTest() { 

         log.warn("uh oh, Job is scheduled !'" + "' Success..."); 

     } 

2 .Quartz 在Spring 中动态设置 cronTrigger 方法一 

Spring 配置文件: 

          

          

    

    

        

          

        

    

      

         

           

              0/10 * * * * ?

         

     

    

        

            

                

            

        

scheduleInfoAction 中的reScheduleJob () 方法及相关方法 

① reScheduleJob 读取数据库,获得自定义定时器调度时间 (): 

     private void reScheduleJob () throws SchedulerException, ParseException { 

         // 运行时可通过动态注入的scheduler 得到trigger 

         CronTriggerBean trigger = (CronTriggerBean) scheduler.getTrigger( 

                "cronTrigger ", Scheduler.DEFAULT_GROUP); 

         String dbCronExpression = getCronExpressionFromDB (); 

         String originConExpression = trigger.getCronExpression(); 

     // 判断从DB 中取得的任务时间(dbCronExpression) 和现在的quartz 线程中的任务时间(originConExpression) 是否相等 

     // 如果相等,则表示用户并没有重新设定数据库中的任务时间,这种情况不需要重新rescheduleJob 

         if(!originConExpression.equalsIgnoreCase(dbCronExpression)){ 

             trigger.setCronExpression(dbCronExpression); 

             scheduler.rescheduleJob("cronTrigger ", Scheduler.DEFAULT_GROUP, trigger); 

         } 

     // 下面是具体的job 内容,可自行设置 

     // executeJobDetail(); 

② getCronExpressionFromDB() :从数据库中获得dbCronExpression 的具体代码,由于使用了scheduleInfoManager ,所以要在定义相应的setter 方法 

     private String getCronExpressionFromDB (){ 

         String sql="from ScheduleInfo scheduleInfo where 1=1 "; 

         sql=sql+" and scheduleInfo.infoId = '"+"1" + "'"; 

         List scheduleList = scheduleInfoManager.queryScheduleInListBySql(sql); 

         ScheduleInfo scheduleInfo = (ScheduleInfo)scheduleList.get(0); 

         String dbCronExpression = scheduleInfo.getCronExpression(); 

         return dbCronExpression; 

③ 在spring 配置文件的scheduleInfoAction 配置了相应的property (scheduler / scheduleInfoManager ), 要为其设置setter 方法 

     private Scheduler scheduler; 

     // 设值注入,通过setter 方法传入被调用者的实例scheduler 

     public void setScheduler (Scheduler scheduler) { 

         this.scheduler = scheduler; 

    } 

     private ScheduleInfoManager scheduleInfoManager; 

     // 设值注入,通过setter 方法传入被调用者的实例scheduleInfoManager 

     public void setScheduleInfoManager (ScheduleInfoManager scheduleInfoManager){ 

         this.scheduleInfoManager = scheduleInfoManager; 

     } 

3 . Quartz 在Spring 中动态设置 cronTrigger 方法二 

在上面的2 中我们可以看到,尽管已经可以动态进行 rescheduleJob 了,不过依然需要我们设置一个 cronExpression ,如果尝试一下拿掉spring 配置中的 

          

              0/10 * * * * ?

         

则容器(如tomcat )启动时会报错。 

实际中我们希望tomcat 启动时就可以直接去读数据库,拿到相应的dbCronExpression ,然后定时执行一个job ,而不希望配置初始的cronExpression ,观察下面的CronTriggerBean ,考虑到cronExpression 需要初始化,如果设定一个类InitializingCronTrigger 继承CronTriggerBean ,然后在这个类中做一些读取DB 的初始化工作(设置cronExpression ),问题就可以解决了。 

Spring 配置文件: 

          

          

    

    

        

          

        

    

     

         

         

           

     

    

        

            

                

            

        

InitializingCronTrigger 中的相关方法 

注意:在注入scheduleInfoManager 属性的时候,我们可以去读取DB 任务时间(之所以放在setter 方法中,是因为需要在设置scheduleInfoManager 后进行getCronExpressionFromDB () ,否则,也可以①② 逻辑把放在类的构造函数中). 

注意InitializingCronTrigger 必须extends CronTriggerBean . 

public class InitializingCronTrigger extends CronTriggerBean implements Serializable { 

     private ScheduleInfoManager scheduleInfoManager; 

     // 设值注入,通过setter 方法传入被调用者的实例scheduleInfoManager 

     public void setScheduleInfoManager (ScheduleInfoManager scheduleInfoManager){ 

         this.scheduleInfoManager = scheduleInfoManager; 

         // 因为在getCronExpressionFromDB 使用到了scheduleInfoManager ,所以 

         // 必须上一行代码设置scheduleInfoManager 后进行getCronExpressionFromDB 

         String cronExpression = getCronExpressionFromDB ();    // ① 

         // 因为extends CronTriggerBean ,此处调用父类方法初始化 cronExpression 

        setCronExpression (cronExpression);                     // ② 

     private String getCronExpressionFromDB (){ 

         String sql="from ScheduleInfo scheduleInfo where 1=1 "; 

         sql=sql+" and scheduleInfo.infoId = '"+"1" + "'"; 

         List scheduleList = scheduleInfoManager.queryScheduleInListBySql(sql); 

         ScheduleInfo scheduleInfo = (ScheduleInfo)scheduleList.get(0); 

         String dbCronExpression = scheduleInfo.getCronExpression(); 

         return dbCronExpression; 

…… 

附表: 

"0 0 12 * * ?" 每天中午12 点触发 

"0 15 10 ? * *" 每天上午10:15 触发 

"0 15 10 * * ?" 每天上午10:15 触发 

"0 15 10 * * ? *" 每天上午10:15 触发 

"0 15 10 * * ? 2005" 2005 年的每天上午10:15 触发 

"0 * 14 * * ?" 在每天下午2 点到下午2:59 期间的每1 分钟触发 

"0 0/5 14 * * ?" 在每天下午2 点到下午2:55 期间的每5 分钟触发 

"0 0/5 14,18 * * ?" 在每天下午2 点到2:55 期间和下午6 点到6:55 期间的每5 分钟触发 

"0 0-5 14 * * ?" 在每天下午2 点到下午2:05 期间的每1 分钟触发 

"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10 和2:44 触发 

"0 15 10 ? * MON-FRI" 周一至周五的上午10:15 触发 

"0 15 10 15 * ?" 每月15 日上午10:15 触发 

"0 15 10 L * ?" 每月最后一日的上午10:15 触发 

"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15 触发 

"0 15 10 ? * 6L 2002-2005" 2002 年至2005 年的每月的最后一个星期五上午10:15 触发 

"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15 触发 

至于每个符号 看看例子就好了. 很简单了. 

文档

spring定时任务

Chapter23.Spring中的定时调度(Scheduling)和线程池(ThreadPooling)23.1.简介Spring包含了对定时调度服务的内置支持类。当前,Spring支持从JDK1.3开始内置的Timer类和QuartzScheduler(http://www.opensymphony.com/quartz/)。二者都可以通过FactoryBean,分别指向Timer或Trigger实例的引用进行配置。更进一步,有个对QuartzScheduler和Timer都有效的工具类可
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top