当学完第二课以后,你欣喜的发现,让jobs工做起来是仍是至关简单的。虽然让jobs运行起来很简单,对于其执行的关键内容仍是须要知道的。它们是IJob接口中的Execute和JobDetails。程序员
当你定义一个实现IJob接口的类的时候,你须要在里面实现实际须要执行的代码。Quartz.NET须要知道关于这代码的各类信息,这样 Quartz.NET才能像你指望的那样工做。这些细节是在JobDetail类中进行了描述,在上一节以及进行了简单的描述。安全
JobDetail由JobBuilder进行实例化的。JobBuilder允许开发人员使用 fluent interface.进行自定义JobDetail细节。并发
让咱们花点时间看Job的机制以及在Quartz.NET中的生命周期。在第一节中已经看到的例子让咱们再看一遍:ide
1 // define the job and tie it to our HelloJob class 2 IJobDetail job = JobBuilder.Create<HelloJob>() 3 .WithIdentity("myJob", "group1") 4 .Build(); 5 6 // Trigger the job to run now, and then every 40 seconds 7 ITrigger trigger = TriggerBuilder.Create() 8 .WithIdentity("myTrigger", "group1") 9 .StartNow() 10 .WithSimpleSchedule(x => x 11 .WithIntervalInSeconds(40) 12 .RepeatForever()) 13 .Build(); 14 15 sched.ScheduleJob(job, trigger);
如今定义一个HelloJob 以下所示:函数
1 public class HelloJob : IJob 2 { 3 public void Execute(IJobExecutionContext context) 4 { 5 Console.WriteLine("HelloJob is executing."); 6 } 7 }
请注意,在这里给scheduler 一个IJobDetail 实例,它只须要一个job实例就能进行运行。当scheduler执行job的时候,scheduler 会在调用Execute方法以前实例化一个job实例。 能实例化job的前提是job类中须要有个无参的构造函数。还有一个须要注意的是,在job类中定义任何数据字段其实没有什么太大的意义,由于在job的运行期间不会保留这些数据字段。ui
看到这你或许会问,那我如何为一个job提供属性和配置信息呢?而且这么能跟踪保持job的执行状态呢?要回答这些问题,关键要弄懂JobDataMap,它是JobDetail中的一部分。编码
JobDataMap中可用于容纳任何数量的您但愿提供给job实例在执行时(可序列化)的对象。JobDataMap实现了IDictionary接口,并为其添加了一些用于存储和检索基本类型的数据的实用方法。spa
下面是JobDataMap 快速上手代码,在添加job到scheduler以前先为JobDataMap添加一些数据 :设计
1 // define the job and tie it to our DumbJob class 2 IJobDetail job = JobBuilder.Create<DumbJob>() 3 .WithIdentity("myJob", "group1") // name "myJob", group "group1" 4 .UsingJobData("jobSays", "Hello World!") 5 .UsingJobData("myFloatValue", 3.141f) 6 .Build();下面是从执行的job中获取JobDataMap 数据code
1 Getting Values from a JobDataMap 2 public class DumbJob : IJob 3 { 4 public void Execute(JobExecutionContext context) 5 { 6 JobKey key = context.JobDetail.Key; 7 8 JobDataMap dataMap = context.JobDetail.JobDataMap; 9 10 string jobSays = dataMap.GetString("jobSays"); 11 float myFloatValue = dataMap.GetFloat("myFloatValue"); 12 13 Console.Error.WriteLine("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue); 14 } 15 }若是你准备使用一个持久的JobStore (JobStore 将在JobStore 部分进行讨论)你应该关注将在JobDataMap放些什么。由于它会被序列化,因此更容易产生版本问题。在标准的版本中是安全的,除此以外,任何人均可以改变你的实体类。因此不得不关心兼容性会被破坏的状况。
因此你能够把AdoJobStore and JobDataMap 放进map中,仅包含着原始函数与字符串数据,所以消除了序列化的各类问题。
因为Quartz默认JobFactory会再job实例化的时候去实例那些带有set属性的,因此当你添加带有set访问的属性的时候会在JobDataMap中建立对应的key将其保存。这样就不要进行显示区指示在execute方法方法中进行映射。
Trigger也有其相关的JobDataMap。当你须要多个Trigger进行调度和重复scheduler 这是很是有用的。每一个Trigger是独立的,而且须要你单独输入配置信息。
JobDataMap 将JobExecutionContext 做为job执行时候的上下文。它里面包含了JobDataMap 和Trigger而且覆盖前面相同的值。
下面是来自JobExecutionContext获取数据的做业执行过程当中合并的JobDataMap的一个简单的例子:
View Code1 public class DumbJob : IJob 2 { 3 public void Execute(IJobExecutionContext context) 4 { 5 JobKey key = context.JobDetail.Key; 6 7 JobDataMap dataMap = context.MergedJobDataMap; // Note the difference from the previous example 8 9 string jobSays = dataMap.GetString("jobSays"); 10 float myFloatValue = dataMap.GetFloat("myFloatValue"); 11 IList<DateTimeOffset> state = (IList<DateTimeOffset>) dataMap["myStateData"]; 12 state.Add(DateTimeOffset.UtcNow); 13 14 Console.Error.WriteLine("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue); 15 } 16 }或者,若是你想依靠的JobFactory“注入”数据映射值到你的类,它可能看起来像这个:
View Code1 public class DumbJob : IJob 2 { 3 public string JobSays { private get; set; } 4 public float FloatValue { private get; set; } 5 6 public void Execute(IJobExecutionContext context) 7 { 8 JobKey key = context.JobDetail.Key; 9 10 JobDataMap dataMap = context.MergedJobDataMap; // Note the difference from the previous example 11 12 IList<DateTimeOffset> state = (IList<DateTimeOffset>) dataMap["myStateData"]; 13 state.Add(DateTimeOffset.UtcNow); 14 15 Console.Error.WriteLine("Instance " + key + " of DumbJob says: " + JobSays + ", and val is: " + FloatValue); 16 } 17 }你会注意到类的整个代码较长,但在execute()方法的代码是干净。人们还能够争辩说,虽然代码越长,它实际上花了更少的编码,若是程序员的IDE用于自动生成的属性,而没必要手工编写单独的调用从JobDataMap中检索值。这是你的选择。
不少用户花费时间是困惑到底是什么构成了“Job实例”。咱们会尽力讲清楚这里,下面有关的工做状态和并发性的部分。
您能够建立一个job类,并经过建立JobDetails的多个实例的调度中存储了不少“实例定义” - 每个都有本身的属性和JobDataMap - 而且他们都增长到scheduler中
例如,你能够建立一个实现所谓的“SalesReportJob”的IJob接口的类。这个job可能会被编码到指望发送给它(经过JobDataMap中)来指定销售报告应根据销售人员的姓名参数。而后它们能够建立多个定义的job(JobDetails),如“SalesReportForJoe”和“SalesReportForMike”具备“乔joe”和“Mike”在相应JobDataMaps做为输入到各自的job指定。
当Trigger启动,将一个JobDetail(实例定义),它会被自动加载,和job类是指经过对调度配置的JobFactory实例化。默认的JobFactory只是调用使用Activator.CreateInstance 调用job类的默认构造函数,而后尝试在匹配的JobDataMap中的键名该类调用setter属性。您可能但愿建立本身的实现的JobFactory来完成的事情,如让你的应用程序的IoC或者DI容器产生/初始化做业实例。
In “Quartz speak”, 咱们指的是每一个存储的JobDetail做为“job定义”或“的JobDetail实例”,咱们指的是每个执行job做为“job实例”或“job定义实例”。一般,若是咱们只是用这个词的“job”,咱们指的是一个名为定义或JobDetail等。当咱们指的是类实现job接口,咱们平时使用的术语“job type”。
如今有一些关于Job的状态数据(aka JobDataMap)和并发性附加说明。能够天剑组合特性到你的job类上,来影响Quartz’的行为。
DisallowConcurrentExecution添加到Job类,告诉Quartz不执行给定的job定义的多个实例(便是指给定的job类)并发的属性。注意这里面的所说,必须当心使用。在上一节的例子中,若是“SalesReportJob”有这个属性,比只有一个“SalesReportForJoe”的实例能够在给定时间执行的,但它能够与“SalesReportForMike”的一个实例,同时执行。约束是基于一个实例定义(JobDetail等),而不是在工做类的实例。可是,它(quartz的设计),决定对类自己携带的属性,由于它决定对类进行怎样进行编译。
PersistJobDataAfterExecution是能够被添加到Job类,告诉quartz更新JobDetail的JobDataMap存储的副本属性在execute()方法成功完成后(未抛出异常),使得一样的job在下一次执行(JobDetail)接收,而不是原先存储的值的更新的值。像DisallowConcurrentExecution属性,这适用于做业定义实例,而不是一个做业类的实例,虽然当时决定让job类携带的属性,由于它每每使对类是如何编码的差别(如'有状态'将须要显式地“理解”的执行方法中的代码)。
若是使用PersistJobDataAfterExecution属性,你应该认真考虑也使用DisallowConcurrentExecution属性,以免留下什么数据时,一样的job(JobDetail)的两个实例并发执行存储可能的混淆(竞争条件)
下面是可用于经过JobDetail等对象的job实例来定义的其余属性的简单总结:
持久性 - 若是job是不可持久的,它会自动从调度中删除,一旦再也不有与之相关的任何活动的触发器。换句话说,非持久job具备一个寿命由其触发的存在的限制。
可恢复性 - 若是做业“要求恢复”,它是在调度的“硬关闭”的时间执行(即它崩溃中运行的过程当中,或在机器关闭),而后从新执行当调度程序从新开始。在这种状况下,JobExecutionContext.Recovering属性将返回真。
最后,咱们须要告诉你的IJob.Execute(..)方法的一些细节。你应该从执行方法抛出的惟一类型是JobExecutionException。正由于如此,你一般应该换execute方法的所有内容以'的try-catch“块。你也应该花一些时间看的JobExecutionException的文档,你的工做能够用它来提供调度各类指令为你想怎么异常进行处理。