spring quartz学习总结: 基本任务结构和定义

来源:互联网 发布:xp限制安装软件 编辑:程序博客网 时间:2024/04/30 08:01

简介

    最近在工作中一些地方要用到quartz做定时任务,于是结合一些示例和自己的理解整理了几个示例。通过对这些基本概念的分析希望能够加深一点对它们的理解。

 

Quartz

    Quartz是一个开源的定时任务框架,在一些日常的任务中,我们经常有一些这样的需要,比如我们需要隔多少分钟去扫描或者检查一下某些文件,或者在指定的时间点内去处理一些文件。这些定时的任务更多的是一种批处理任务。在一些常用的实现里我们会使用shell脚本来完成这份工作。使用shell脚本来完成的这些任务相对来说是特定于某个任务的,不具备通用性,而且根据问题性质的复杂程度,有时候我们需要提高定时任务的可靠性和性能,这个时候单纯的用一些脚本来处理就显得非常困难和复杂了 。于是quartz这个框架就能帮我们不少忙。

 

第一个示例

    quartz本身的概念并不复杂,我们可以通过一个示例来结合着过一遍。首先我们建一个maven的工程,设置其archetype为quickstart。其中的pom.xml文件内容如下:

 

Xml代码  收藏代码
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  3.   <modelVersion>4.0.0</modelVersion>  
  4.   
  5.   <groupId>com.yunzero</groupId>  
  6.   <artifactId>SpringQuartzSample</artifactId>  
  7.   <version>0.0.1-SNAPSHOT</version>  
  8.   <packaging>jar</packaging>  
  9.   
  10.   <name>SpringQuartzSample</name>  
  11.   <url>http://maven.apache.org</url>  
  12.   
  13.   <properties>  
  14.     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
  15.   </properties>  
  16.   
  17.   <dependencies>  
  18.     <dependency>  
  19.         <groupId>org.quartz-scheduler</groupId>  
  20.         <artifactId>quartz</artifactId>  
  21.         <version>2.2.1</version>  
  22.     </dependency>  
  23.     <dependency>  
  24.         <groupId>org.quartz-scheduler</groupId>  
  25.         <artifactId>quartz-jobs</artifactId>  
  26.         <version>2.2.1</version>  
  27.     </dependency>  
  28.     <dependency>  
  29.       <groupId>junit</groupId>  
  30.       <artifactId>junit</artifactId>  
  31.       <version>4.11</version>  
  32.       <scope>test</scope>  
  33.     </dependency>  
  34.   </dependencies>  
  35. </project>  

    这里的内容很简单,主要是添加了对quartz的依赖。

    配置好引用之后,我们创建一个类HelloJob:

Java代码  收藏代码
  1. package com.yunzero;  
  2.   
  3. import org.quartz.Job;  
  4. import org.quartz.JobExecutionContext;  
  5. import org.quartz.JobExecutionException;  
  6.   
  7.   
  8. public class HelloJob implements Job {  
  9.   
  10.     public void execute(JobExecutionContext context) throws JobExecutionException {  
  11.         System.out.println("job started...");  
  12.     }  
  13. }  

     在quartz里,我们具体要执行的任务就是通过实现接口Job来定义的。在这里的实现仅仅是打印一个字符串。

    定义好需要执行的任务之后,我们需要的是来定义任务执行的细节了。既然quartz是用来实现计划任务的功能,那么很多执行计划任务需要考虑到的东西就应该在这里定义了。比如说既然这个任务定义好了,它什么时候开始执行呢?需要执行一次还是多少次呢?每次执行的周期是多久呢?于是我们调用执行这个任务的代码实现如下:

Java代码  收藏代码
  1. package com.yunzero;  
  2.   
  3. import org.quartz.CronScheduleBuilder;  
  4. import org.quartz.JobBuilder;  
  5. import org.quartz.JobDetail;  
  6. import org.quartz.Scheduler;  
  7. import org.quartz.SchedulerFactory;  
  8. import org.quartz.Trigger;  
  9. import org.quartz.TriggerBuilder;  
  10. import org.quartz.impl.StdSchedulerFactory;  
  11.   
  12. public class App   
  13. {  
  14.     public static void main( String[] args ) throws Exception  
  15.     {  
  16.         // Define scheduler  
  17.         SchedulerFactory sf = new StdSchedulerFactory();  
  18.         Scheduler sched = sf.getScheduler();  
  19.   
  20.         // Define job  
  21.         JobDetail job = JobBuilder.newJob(HelloJob.class)  
  22.                 .withIdentity("job1""group1")  
  23.                 .build();  
  24.   
  25.         // Define trigger strategy  
  26.         Trigger trigger = TriggerBuilder.newTrigger()  
  27.                 .withIdentity("trigger1""group1")  
  28.                 .withSchedule(CronScheduleBuilder.cronSchedule("0/20 * * * * ?"))  
  29.                 .build();  
  30.         sched.scheduleJob(job, trigger);  
  31.         // Start the job  
  32.         sched.start();  
  33.         Thread.sleep(90L * 1000L);  
  34.         sched.shutdown(true);  
  35.     }  
  36. }  

     上述的代码其实可以定义成4个部分,第1部分是创建一个Scheduler对象,它用来真正调度和执行job。然后第2部分是注册我们定义的job,这里引用了我们前面定义的HelloJob.class。第3部分则定义触发这些任务的细节。这里的CronScheduleBuilder定义了该任务每20秒执行一次。第4部分则是通过scheduler启动job。sched.scheduleJob()方法里有两个参数,分别引用了job对象和trigger对象。在实际中我们可以实现一个很好的job和trigger的分离,将需要的job和对应的trigger绑定起来。

     就这么几个简单的步骤,我们定义的一个计划任务就完成了。它会每20秒打印一段字符串。然后主线程sleep 90秒之后再退出quartz。如果我们执行上述的代码,其运行结果如下:

 

Shell代码  收藏代码
  1. SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".  
  2. SLF4J: Defaulting to no-operation (NOP) logger implementation  
  3. SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.  
  4. job started...  

     结合前面的代码和执行结果,我们可以发现最常用的quartz的过程主要就是scheduler, job, trigger它们三个之间来处理的。因此,只要把握好这个就可以实现一个理想的定时调度任务。

 

结合spring

    上面的这个示例只是用java调用的原生quartz api实现的。如果使用spring的话,这个框架本身对集成quartz有更好的支持。再来看看结合spring实现定时任务的示例。

     我们再创建一个maven的工程,其类型依然是archetype-quickstart。唯一不同的地方在于我们引入更多spring的库,其pom.xml文件内容如下:

 

Xml代码  收藏代码
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  3.   <modelVersion>4.0.0</modelVersion>  
  4.   
  5.   <groupId>com.yunzero.spring</groupId>  
  6.   <artifactId>SpringQuartzIntegrationExample</artifactId>  
  7.   <version>1.0.0</version>  
  8.   <packaging>jar</packaging>  
  9.   <name>SpringQuartzIntegrationExample</name>  
  10.   
  11.     <properties>  
  12.         <springframework.version>4.0.6.RELEASE</springframework.version>  
  13.         <quartz.version>2.2.1</quartz.version>  
  14.     </properties>  
  15.   
  16.     <dependencies>  
  17.         <dependency>  
  18.             <groupId>org.springframework</groupId>  
  19.             <artifactId>spring-core</artifactId>  
  20.             <version>${springframework.version}</version>  
  21.         </dependency>  
  22.         <dependency>  
  23.             <groupId>org.springframework</groupId>  
  24.             <artifactId>spring-context-support</artifactId>  
  25.             <version>${springframework.version}</version>  
  26.         </dependency>  
  27.         <!-- Transaction dependency is required with Quartz integration -->  
  28.         <dependency>  
  29.             <groupId>org.springframework</groupId>  
  30.             <artifactId>spring-tx</artifactId>  
  31.             <version>${springframework.version}</version>  
  32.         </dependency>  
  33.           
  34.         <!-- Quartz framework -->  
  35.         <dependency>  
  36.             <groupId>org.quartz-scheduler</groupId>  
  37.             <artifactId>quartz</artifactId>  
  38.             <version>${quartz.version}</version>  
  39.         </dependency>  
  40.   
  41.     </dependencies>  
  42. </project>  

     这里除了引用了几个spring相关的库以外和前面的示例没太大差别。

    我们再来看job的定义。在spring里,定义quartz job的方式稍微有点不一样,它是需要继承类QuartzJobBean,我们这里的一个实现如下:

 

Java代码  收藏代码
  1. package com.yunzero.spring.quartz;  
  2.   
  3. import org.quartz.JobExecutionContext;  
  4. import org.quartz.JobExecutionException;  
  5. import org.springframework.scheduling.quartz.QuartzJobBean;  
  6.   
  7. import com.websystique.spring.scheduling.AnotherBean;  
  8.   
  9. public class ScheduledJob extends QuartzJobBean{  
  10.   
  11.     private AnotherBean anotherBean;   
  12.       
  13.     @Override  
  14.     protected void executeInternal(JobExecutionContext arg0)  
  15.             throws JobExecutionException {  
  16.         anotherBean.printAnotherMessage();  
  17.     }  
  18.   
  19.     public void setAnotherBean(AnotherBean anotherBean) {  
  20.         this.anotherBean = anotherBean;  
  21.     }  
  22. }  

    这部分代码就是一个典型的spring注入,实际上调用了anotherBean的方法。具体job的执行是在executeInternal方法里。而AnotherBean的实现也很简单:

Java代码  收藏代码
  1. package com.yunzero.spring.scheduling;  
  2.   
  3. import org.springframework.stereotype.Component;  
  4.   
  5. @Component("anotherBean")  
  6. public class AnotherBean {  
  7.       
  8.     public void printAnotherMessage(){  
  9.         System.out.println("I am called by Quartz jobBean using CronTriggerFactoryBean");  
  10.     }  
  11.       
  12. }  

    因为是要将这些任务通过spring的配置文件来拼接到一起,我们来看看具体的配置文件该怎么设置。

    在spring里,如果我们要执行一个计划任务,需要定义一个JobDetail,用它来封装我们具体执行的任务。结合前面纯quartz的示例,我们发现它们其实本质上是一样的。这里的定义如下:

Xml代码  收藏代码
  1. <!-- For times when you just need to invoke a method on a specific object -->  
  2.     <bean id="simpleJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
  3.         <property name="targetObject" ref="myBean" />  
  4.         <property name="targetMethod" value="printMessage" />  
  5.     </bean>  

   spring默认提供了一个叫MethodInvokingJobDetailFactoryBean,我们需要将定义好的对象和需要调用的方法传给它。这里对应的是一种类型的jobDetail定义。对应的myBean定义如下:

Java代码  收藏代码
  1. package com.yunzero.spring.scheduling;  
  2.   
  3. import org.springframework.stereotype.Component;  
  4.   
  5. @Component("myBean")  
  6. public class MyBean {  
  7.   
  8.     public void printMessage() {  
  9.         System.out.println("I am called by MethodInvokingJobDetailFactoryBean using SimpleTriggerFactoryBean");  
  10.     }  
  11.       
  12. }  

     这种封装的方式很简单,就是我定义了一个对象和它对应的方法。如果我们需要将它们封装成一个job了,只要把类的名字和对应的方法传进去就可以了。

    除了上述的JobDetail构造方式,还要一种更复杂一些,它的定义如下:

Xml代码  收藏代码
  1. <!-- For times when you need more complex processing, passing data to the scheduled job -->  
  2.     <bean name="complexJobDetail"    class="org.springframework.scheduling.quartz.JobDetailFactoryBean">  
  3.         <property name="jobClass" value="com.yunzero.spring.quartz.ScheduledJob" />  
  4.         <property name="jobDataMap">  
  5.             <map>  
  6.                 <entry key="anotherBean" value-ref="anotherBean" />  
  7.             </map>  
  8.         </property>  
  9.         <property name="durability" value="true" />  
  10.     </bean>  

     这里因为要用到一些对象的引用,对这些对象或者参数的传递可以通过jobDataMap来处理。

    和纯手工写的步骤一样,定义好了jobDetail之后就需要定义trigger了。

 

Trigger定义

    在spring里也有两种常见的trigger定义,一种为simpleTrigger,其定义如下:

Xml代码  收藏代码
  1. <!-- Run the job every 2 seconds with initial delay of 1 second -->  
  2.     <bean id="simpleTrigger"  class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">  
  3.         <property name="jobDetail" ref="simpleJobDetail" />  
  4.         <property name="startDelay" value="1000" />  
  5.         <property name="repeatInterval" value="2000" />  
  6.     </bean>  

   这里表示对应前面那个simpleDetail的任务,它首先延迟1秒钟然后启动,每2秒钟执行一次这个任务。

   还有一种trigger的定义是cronjob的方式定义的,它的一个配置示例如下:

Xml代码  收藏代码
  1. <!-- Run the job every 5 seconds only on weekends -->  
  2.     <bean id="cronTrigger"  class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">  
  3.         <property name="jobDetail" ref="complexJobDetail" />  
  4.         <property name="cronExpression" value="0/5 * * ? * SAT-SUN" />  
  5.     </bean>  

    在这个示例里,我们用这个trigger来驱动前面那个复杂的jobDetail。如果定义cronTrigger,需要设置cronExpression。比如这里设定该任务只在周末执行,且每5秒钟执行一次。

    按照我们理解的套路,要让这些定义好的job能够跑起来,我们还需要有一个scheduler,在spring里,这些也是准备好了的。针对前面的两个jobDetail和trigger,它的典型配置如下:

 

Xml代码  收藏代码
  1. <!-- Scheduler factory bean to glue together jobDetails and triggers to Configure Quartz Scheduler -->  
  2.     <bean  class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
  3.         <property name="jobDetails">  
  4.             <list>  
  5.                 <ref bean="simpleJobDetail" />  
  6.                 <ref bean="complexJobDetail" />  
  7.             </list>  
  8.         </property>  
  9.   
  10.         <property name="triggers">  
  11.             <list>  
  12.                 <ref bean="simpleTrigger" />  
  13.                 <ref bean="cronTrigger" />  
  14.             </list>  
  15.         </property>  
  16.     </bean>  

   这样,到这里所有的配置就完成了。而启动整个任务的过程和使用spring的普通应用一样:

 

Java代码  收藏代码
  1. package com.yunzero.spring;  
  2.   
  3. import org.springframework.context.support.AbstractApplicationContext;  
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  5.   
  6. public class AppMain {  
  7.     public static void main(String args[]){  
  8.         AbstractApplicationContext context = new ClassPathXmlApplicationContext("quartz-context.xml");  
  9.     }  
  10.   
  11. }  

 

 执行这个应用程序,会得到结果如下:

 

Java代码  收藏代码
  1. Dec 212014 10:03:11 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh  
  2. INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4f9f6e39: startup date [Sun Dec 21 22:03:11 CST 2014]; root of context hierarchy  
  3. Dec 212014 10:03:11 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions  
  4. INFO: Loading XML bean definitions from class path resource [quartz-context.xml]  
  5. SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".  
  6. SLF4J: Defaulting to no-operation (NOP) logger implementation  
  7. SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.  
  8. Dec 212014 10:03:12 PM org.springframework.context.support.DefaultLifecycleProcessor start  
  9. INFO: Starting beans in phase 2147483647  
  10. Dec 212014 10:03:12 PM org.springframework.scheduling.quartz.SchedulerFactoryBean startScheduler  
  11. INFO: Starting Quartz Scheduler now  
  12. I am called by MethodInvokingJobDetailFactoryBean using SimpleTriggerFactoryBean  
  13. I am called by Quartz jobBean using CronTriggerFactoryBean  
  14. I am called by MethodInvokingJobDetailFactoryBean using SimpleTriggerFactoryBean  
  15. I am called by MethodInvokingJobDetailFactoryBean using SimpleTriggerFactoryBean  
  16. I am called by MethodInvokingJobDetailFactoryBean using SimpleTriggerFactoryBean  

    除了前面的那些输出的日志,这里每2秒钟会输出simpleJobDetail的内容,每5秒钟则会输出complexDetail的那个任务的内容。

 

总结

    在这两个示例里,我们首先通过一个纯手工的过程来完成一个任务调度的示例。它的主要步骤为1.定义job 2. 定义trigger 3. 定义scheduler来拼接。在后续使用spring的示例里,其实也是这么一个步骤,只不过spring提供了一些实现的支持,需要在配置文件里指定不同的jobDetail类型和trigger类型。

 

参考材料

http://websystique.com/spring/spring-4-quartz-scheduler-integration-example/

http://docs.spring.io/spring/docs/4.1.4.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/#scheduling-quartz



转:http://shmilyaw-hotmail-com.iteye.com/blog/2169047

0 0
原创粉丝点击