مرحبا مرة اخرى خاصة لطلاب الدورة التدريبية "Developer on the Spring Framework" أعدوا ترجمة لمقال مثير للاهتمام.
في مقالي
"المواصفات إلى الإنقاذ" ، أوضحت كيف يمكنك استخدام مواصفات JPA في Spring Boot لتنفيذ التصفية في واجهة برمجة تطبيقات RESTful. ثم في مقال
"اختبار هذه المواصفات" ، تم عرض كيفية اختبار هذه المواصفات نفسها.
في الخطوة التالية ، قررت توضيح كيفية إضافة برنامج جدولة المهام إلى نفس تطبيق Spring Boot.
كوارتز جدولة المهام
يستمر فريق Spring في تسهيل تطوير Java من خلال توفير إضافات Spring Spring Starter من خلال التبعية البسيطة.
في هذه المقالة ، سأركز على كاتب
كوارتز Scheduler ، الذي يمكن إضافته إلى مشروع Spring Boot بالتبعية التالية:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
التنفيذ بسيط للغاية وهو موصوف
هنا . يمكنك الاطلاع على القائمة الكاملة لبرنامج Spring Boot Starter الحالي
هنا .
تعديل
باستخدام ورقة نشرها
David Kiss ، تتمثل الخطوة الأولى في إضافة الربط التلقائي لوظائف Quartz:
public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware { private transient AutowireCapableBeanFactory beanFactory; @Override public void setApplicationContext(final ApplicationContext context) { beanFactory = context.getAutowireCapableBeanFactory(); } @Override protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { final Object job = super.createJobInstance(bundle); beanFactory.autowireBean(job); return job; } }
بعد ذلك ، أضف التكوين كوارتز الأساسي:
@Configuration public class QuartzConfig { private ApplicationContext applicationContext; private DataSource dataSource; public QuartzConfig(ApplicationContext applicationContext, DataSource dataSource) { this.applicationContext = applicationContext; this.dataSource = dataSource; } @Bean public SpringBeanJobFactory springBeanJobFactory() { AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); jobFactory.setApplicationContext(applicationContext); return jobFactory; } @Bean public SchedulerFactoryBean scheduler(Trigger... triggers) { SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean(); Properties properties = new Properties(); properties.setProperty("org.quartz.scheduler.instanceName", "MyInstanceName"); properties.setProperty("org.quartz.scheduler.instanceId", "Instance1"); schedulerFactory.setOverwriteExistingJobs(true); schedulerFactory.setAutoStartup(true); schedulerFactory.setQuartzProperties(properties); schedulerFactory.setDataSource(dataSource); schedulerFactory.setJobFactory(springBeanJobFactory()); schedulerFactory.setWaitForJobsToCompleteOnShutdown(true); if (ArrayUtils.isNotEmpty(triggers)) { schedulerFactory.setTriggers(triggers); } return schedulerFactory; } }
يمكنك إخراج الخصائص المستخدمة في طريقة
scheduler()
، لكنني قررت على وجه التحديد تبسيط هذا المثال.
ثم تتم إضافة الأساليب الثابتة التي توفر طريقة برمجية لإنشاء المهام والمشغلات:
@Slf4j @Configuration public class QuartzConfig { ... static SimpleTriggerFactoryBean createTrigger(JobDetail jobDetail, long pollFrequencyMs, String triggerName) { log.debug("createTrigger(jobDetail={}, pollFrequencyMs={}, triggerName={})", jobDetail.toString(), pollFrequencyMs, triggerName); SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean(); factoryBean.setJobDetail(jobDetail); factoryBean.setStartDelay(0L); factoryBean.setRepeatInterval(pollFrequencyMs); factoryBean.setName(triggerName); factoryBean.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY); factoryBean.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT); return factoryBean; } static CronTriggerFactoryBean createCronTrigger(JobDetail jobDetail, String cronExpression, String triggerName) { log.debug("createCronTrigger(jobDetail={}, cronExpression={}, triggerName={})", jobDetail.toString(), cronExpression, triggerName);
طريقة
createJobDetail()
هي طريقة بسيطة ومفيدة لإنشاء المهام.
هناك خياران للمشغلات: يعتمد على CRON والمشغلات البسيطة.
الخدمات
أصبح برنامج جدولة قاعدة الكوارتز جاهزًا الآن لتشغيل المهام في تطبيق Spring Boot. بعد ذلك ، سنقوم بإنشاء بعض أمثلة الخدمات التي سيتم إطلاقها بواسطة المجدول.
تعرض الخدمة الأولى إحصائيات عضوية بسيطة. إذا كنت تتذكر ، فإن المثال في المشروع الأصلي كان متعلقًا بنادي للياقة البدنية. في فئة
MemberService
بإنشاء أسلوب
memberStats()
:
public void memberStats() { List<Member> members = memberRepository.findAll(); int activeCount = 0; int inactiveCount = 0; int registeredForClassesCount = 0; int notRegisteredForClassesCount = 0; for (Member member : members) { if (member.isActive()) { activeCount++; if (CollectionUtils.isNotEmpty(member.getMemberClasses())) { registeredForClassesCount++; } else { notRegisteredForClassesCount++; } } else { inactiveCount++; } } log.info("Member Statics:"); log.info("=============="); log.info("Active member count: {}", activeCount); log.info(" - Registered for Classes count: {}", registeredForClassesCount); log.info(" - Not registered for Classes count: {}", notRegisteredForClassesCount); log.info("Inactive member count: {}", inactiveCount); log.info("=========================="); }
لتتبع الاهتمامات في فصول نادي اللياقة البدنية ، قم بإنشاء طريقة
classStats()
في
classStats()
:
public void classStats() { List<MemberClass> memberClasses = classRepository.findAll(); Map<String, Integer> memberClassesMap = memberClasses .stream() .collect(Collectors.toMap(MemberClass::getName, c -> 0)); List<Member> members = memberRepository.findAll(); for (Member member : members) { if (CollectionUtils.isNotEmpty(member.getMemberClasses())) { for (MemberClass memberClass : member.getMemberClasses()) { memberClassesMap.merge(memberClass.getName(), 1, Integer::sum); } } } log.info("Class Statics:"); log.info("============="); memberClassesMap.forEach((k,v) -> log.info("{}: {}", k, v)); log.info("=========================="); }
المهام
لتشغيل رمز الخدمة ، يجب عليك إنشاء الوظيفة المناسبة. بالنسبة إلى
MemberService
قمت بإنشاء
MemberStatsJob
وظيفة
MemberStatsJob
:
@Slf4j @Component @DisallowConcurrentExecution public class MemberStatsJob implements Job { @Autowired private MemberService memberService; @Override public void execute(JobExecutionContext context) { log.info("Job ** {} ** starting @ {}", context.getJobDetail().getKey().getName(), context.getFireTime()); memberService.memberStats(); log.info("Job ** {} ** completed. Next job scheduled @ {}", context.getJobDetail().getKey().getName(), context.getNextFireTime()); } }
من أجل خدمة
MemberClassService
، تم إنشاء فئة
MemberClassService
:
@Slf4j @Component @DisallowConcurrentExecution public class MemberClassStatsJob implements Job { @Autowired MemberClassService memberClassService; @Override public void execute(JobExecutionContext context) { log.info("Job ** {} ** starting @ {}", context.getJobDetail().getKey().getName(), context.getFireTime()); memberClassService.classStats(); log.info("Job ** {} ** completed. Next job scheduled @ {}", context.getJobDetail().getKey().getName(), context.getNextFireTime()); } }
جدول المهام
في هذا المشروع ، نريد أن يتم جدولة جميع المهام عند بدء تشغيل خادم Spring Boot. للقيام بذلك ، قمت بإنشاء فئة
QuartzSubmitJobs
، والتي تتضمن أربع طرق بسيطة. طريقتين إنشاء مهام جديدة ، وطريقتين إنشاء مشغلات المقابلة.
@Configuration public class QuartzSubmitJobs { private static final String CRON_EVERY_FIVE_MINUTES = "0 0/5 * ? * * *"; @Bean(name = "memberStats") public JobDetailFactoryBean jobMemberStats() { return QuartzConfig.createJobDetail(MemberStatsJob.class, "Member Statistics Job"); } @Bean(name = "memberStatsTrigger") public SimpleTriggerFactoryBean triggerMemberStats(@Qualifier("memberStats") JobDetail jobDetail) { return QuartzConfig.createTrigger(jobDetail, 60000, "Member Statistics Trigger"); } @Bean(name = "memberClassStats") public JobDetailFactoryBean jobMemberClassStats() { return QuartzConfig.createJobDetail(MemberClassStatsJob.class, "Class Statistics Job"); } @Bean(name = "memberClassStatsTrigger") public CronTriggerFactoryBean triggerMemberClassStats(@Qualifier("memberClassStats") JobDetail jobDetail) { return QuartzConfig.createCronTrigger(jobDetail, CRON_EVERY_FIVE_MINUTES, "Class Statistics Trigger"); } }
إطلاق الحذاء الربيع
عندما يكون كل شيء جاهزًا ، يمكنك بدء تشغيل خادم Spring Boot ومشاهدة تهيئة كوارتز:
2019-07-14 14:36:51.651 org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'MyInstanceName' initialized from an externally provided properties instance. 2019-07-14 14:36:51.651 org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.0 2019-07-14 14:36:51.651 org.quartz.core.QuartzScheduler : JobFactory set to: com.gitlab.johnjvester.jpaspec.config.AutowiringSpringBeanJobFactory@79ecc507 2019-07-14 14:36:51.851 ossconcurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2019-07-14 14:36:51.901 aWebConfiguration$JpaWebMvcConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning 2019-07-14 14:36:52.051 ossquartz.SchedulerFactoryBean : Starting Quartz Scheduler now 2019-07-14 14:36:52.054 ossquartz.LocalDataSourceJobStore : Freed 0 triggers from 'acquired' / 'blocked' state. 2019-07-14 14:36:52.056 ossquartz.LocalDataSourceJobStore : Recovering 0 jobs that were in-progress at the time of the last shut-down. 2019-07-14 14:36:52.056 ossquartz.LocalDataSourceJobStore : Recovery complete. 2019-07-14 14:36:52.056 ossquartz.LocalDataSourceJobStore : Removed 0 'complete' triggers. 2019-07-14 14:36:52.058 ossquartz.LocalDataSourceJobStore : Removed 0 stale fired job entries. 2019-07-14 14:36:52.058 org.quartz.core.QuartzScheduler : Scheduler MyInstanceName_$_Instance1 started.
وتشغيل
memberStats()
:
2019-07-14 14:36:52.096 cgjjpaspec.jobs.MemberStatsJob : Job ** Member Statistics Job ** starting @ Sun Jul 14 14:36:52 EDT 2019 2019-07-14 14:36:52.217 cgjjpaspec.service.MemberService : Member Statics: 2019-07-14 14:36:52.217 cgjjpaspec.service.MemberService : ============== 2019-07-14 14:36:52.217 cgjjpaspec.service.MemberService : Active member count: 7 2019-07-14 14:36:52.217 cgjjpaspec.service.MemberService : - Registered for Classes count: 6 2019-07-14 14:36:52.217 cgjjpaspec.service.MemberService : - Not registered for Classes count: 1 2019-07-14 14:36:52.217 cgjjpaspec.service.MemberService : Inactive member count: 3 2019-07-14 14:36:52.217 cgjjpaspec.service.MemberService : ========================== 2019-07-14 14:36:52.219 cgjjpaspec.jobs.MemberStatsJob : Job ** Member Statistics Job ** completed. Next job scheduled @ Sun Jul 14 14:37:51 EDT 2019
ثم القيام
classStats()
:
2019-07-14 14:40:00.006 cgjjpaspec.jobs.MemberClassStatsJob : Job ** Class Statistics Job ** starting @ Sun Jul 14 14:40:00 EDT 2019 2019-07-14 14:40:00.021 cgjjservice.MemberClassService : Class Statics: 2019-07-14 14:40:00.022 cgjjservice.MemberClassService : ============= 2019-07-14 14:40:00.022 cgjjservice.MemberClassService : Tennis: 4 2019-07-14 14:40:00.022 cgjjservice.MemberClassService : FitCore 2000: 3 2019-07-14 14:40:00.022 cgjjservice.MemberClassService : Spin: 2 2019-07-14 14:40:00.022 cgjjservice.MemberClassService : Swimming: 4 2019-07-14 14:40:00.022 cgjjservice.MemberClassService : New Class: 0 2019-07-14 14:40:00.022 cgjjservice.MemberClassService : Basketball: 2 2019-07-14 14:40:00.022 cgjjservice.MemberClassService : ========================== 2019-07-14 14:40:00.022 cgjjpaspec.jobs.MemberClassStatsJob : Job ** Class Statistics Job ** completed. Next job scheduled @ Sun Jul 14 14:45:00 EDT 2019
استنتاج
في المثال أعلاه ، استخدمت مشروعًا موجودًا في برنامج Spring Boot وبدون جهد كبير أضافت جدولة كوارتز إليه. أنا خلقت أساليب الخدمة التي أجريت تحليل البيانات بسيط. تم إطلاق أساليب الخدمة هذه بواسطة فئات الوظائف. أخيرًا ، تم جدولة الوظائف والمشغلات.
يمكن العثور على شفرة المصدر الكاملة
هنا .
في
المقالة التالية ، سأوضح كيفية إضافة واجهة برمجة تطبيقات RESTful لعرض معلومات إعدادات كوارتز.