最近看到一线码农大佬翻译的《如何在 ASP.NET Core 中使用 Quartz.NET 执行任务调度》
行文思路:
- 安装Quartz.NET
- Quartz.NET 中的Job,triggers 和 Schedulers
- 创建 Scheduler
- 开启和停止 scheduler
- 创建 job 工厂
- 创建 JobMetadata 存储你的 job 元数据
不可否认,一线大佬的翻译文还是相当精准的, 但个人认为这篇文章的底稿有点硬输出,并没有以一个流畅、直观的编码思路来讲述[如何在ASP.NET Core中使用Quartz.NET 执行定时任务]。
尤其是下面这段:
想起我之前也写了《ASP.NET Core+Quartz.Net实现web定时任务》, 文章以一个简单的定时任务讲述了Quartz.NET在ASP.NET Core中的应用思路,遇河架桥,遇山开路。
这里我要解释一下上图中:为什么要自定义一个Job工厂?
先看下官方JobFactory的作用:
大意是说:
如果某触发器被触发,该触发器关联的Job将被调度器上配置的JobFactory
初始化;
Quartz.NET默认的SimpleJobFactory
工厂类,是利用反射+无参构造函数构造出Job实例。
翻源码:
//----------------选自Quartz.Simpl.SimpleJobFactory类------------- using System; using Quartz.Logging; using Quartz.Spi; using Quartz.Util; namespace Quartz.Simpl { /// <summary> /// The default JobFactory used by Quartz - simply calls /// <see cref="ObjectUtils.InstantiateType{T}" /> on the job class. /// </summary> /// <seealso cref="IJobFactory" /> /// <seealso cref="PropertySettingJobFactory" /> /// <author>James House</author> /// <author>Marko Lahma (.NET)</author> public class SimpleJobFactory : IJobFactory { private static readonly ILog log = LogProvider.GetLogger(typeof (SimpleJobFactory)); /// <summary> /// Called by the scheduler at the time of the trigger firing, in order to /// produce a <see cref="IJob" /> instance on which to call Execute. /// </summary> /// <remarks> /// It should be extremely rare for this method to throw an exception - /// basically only the case where there is no way at all to instantiate /// and prepare the Job for execution. When the exception is thrown, the /// Scheduler will move all triggers associated with the Job into the /// <see cref="TriggerState.Error" /> state, which will require human /// intervention (e.g. an application restart after fixing whatever /// configuration problem led to the issue with instantiating the Job). /// </remarks> /// <param name="bundle">The TriggerFiredBundle from which the <see cref="IJobDetail" /> /// and other info relating to the trigger firing can be obtained.</param> /// <param name="scheduler"></param> /// <returns>the newly instantiated Job</returns> /// <throws> SchedulerException if there is a problem instantiating the Job. </throws> public virtual IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { IJobDetail jobDetail = bundle.JobDetail; Type jobType = jobDetail.JobType; try { if (log.IsDebugEnabled()) { log.Debug($"Producing instance of Job '{jobDetail.Key}', class={jobType.FullName}"); } return ObjectUtils.InstantiateType<IJob>(jobType); } catch (Exception e) { SchedulerException se = new SchedulerException($"Problem instantiating class '{jobDetail.JobType.FullName}'", e); throw se; } } /// <summary> /// Allows the job factory to destroy/cleanup the job if needed. /// No-op when using SimpleJobFactory. /// </summary> public virtual void ReturnJob(IJob job) { var disposable = job as IDisposable; disposable?.Dispose(); } } } //------------------节选自Quartz.Util.ObjectUtils类------------------------- public static T InstantiateType<T>(Type type) { if (type == null) { throw new ArgumentNullException(nameof(type), "Cannot instantiate null"); } ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes); if (ci == null) { throw new ArgumentException("Cannot instantiate type which has no empty constructor", type.Name); } return (T) ci.Invoke(new object[0]); }
但是很多情况下我们定义的Job很可能依赖第三方服务,就比如一线大佬文中NotificationJob
依赖了ILogger<NotificationJob>
服务。
这样默认的SimpleJobFactory
不能满足实例化要求, 考虑将Job任务作为依赖注入组件,加入依赖注入容器。
关键思路:
IScheduler 开放了JobFactory 属性,便于你应用自定义的Job工厂;
在自定义Job工厂中,使用ASP.NET Core依赖注入容器IServiceProvider
解析出特定的Job。