做暧网站,教育发展基金会网站建设,网站更改公司需要重新备案吗,抓取网站访客数据原理1 需求在我的前后端分离的实验室管理项目中#xff0c;有一个功能是学生状态统计。我的设计是按天统计每种状态的比例。为了便于计算#xff0c;在每天0点#xff0c;系统需要将学生的状态重置#xff0c;并插入一条数据作为一天的开始状态。另外#xff0c;考虑到学生的请…1 需求在我的前后端分离的实验室管理项目中有一个功能是学生状态统计。我的设计是按天统计每种状态的比例。为了便于计算在每天0点系统需要将学生的状态重置并插入一条数据作为一天的开始状态。另外考虑到学生的请假需求请假的申请往往是提前做好等系统时间走到实际请假时间的时候系统要将学生的状态修改为请假。显然这两个子需求都可以通过定时任务实现。在网上略做搜索以后我选择了比较流行的定时任务框架Quartz。2 QuartzQuartz是一个定时任务框架其他介绍网上也很详尽。这里要介绍一下Quartz里的几个非常核心的接口。2.1 Scheduler接口Scheduler翻译成调度器Quartz通过调度器来注册、暂停、删除Trigger和JobDetail。Scheduler还拥有一个SchedulerContext顾名思义就是上下文通过SchedulerContext我们可以获取到触发器和任务的一些信息。2.2 Trigger接口Trigger可以翻译成触发器通过cron表达式或是SimpleScheduleBuilder等类指定任务执行的周期。系统时间走到触发器指定的时间的时候触发器就会触发任务的执行。2.3 JobDetail接口Job接口是真正需要执行的任务。JobDetail接口相当于将Job接口包装了一下Trigger和Scheduler实际用到的都是JobDetail。3 SpringBoot官方文档解读SpringBoot官方写了 spring-boot-starter-quartz 。使用过SpringBoot的同学都知道这是一个官方提供的启动器有了这个启动器集成的操作就会被大大简化。现在我们来看一看SpingBoot2.2.6官方文档其中第4.20小节 Quartz Scheduler 就谈到了Quartz但很可惜一共只有两页不到的内容先来看看这么精华的文档里能学到些什么。Spring Boot offers several conveniences for working with the Quartz scheduler, including the
spring-boot-starter-quartz “Starter”. If Quartz is available, a Scheduler is auto-configured (through the SchedulerFactoryBean abstraction).
Beans of the following types are automatically picked up and associated with the Scheduler:
• JobDetail: defines a particular Job. JobDetail instances can be built with the JobBuilder API.
• Calendar.
• Trigger: defines when a particular job is triggered.翻译一下SpringBoot提供了一些便捷的方法来和Quartz协同工作这些方法里面包括spring-boot-starter-quartz这个启动器。如果Quartz可用Scheduler会通过SchedulerFactoryBean这个工厂bean自动配置到SpringBoot里。
JobDetail、Calendar、Trigger这些类型的bean会被自动采集并关联到Scheduler上。
Jobs can define setters to inject data map properties. Regular beans can also be injected in a similar manner.翻译一下Job可以定义setter(也就是set方法)来注入配置信息。也可以用同样的方法注入普通的bean。下面是文档里给的示例代码我直接完全照着写拿到的却是null。不知道是不是我的使用方式有误。后来仔细一想文档的意思应该是在创建Job对象之后调用set方法将依赖注入进去。但后面我们是通过框架反射生成的Job对象这样做反而会搞得更加复杂。最后还是决定采用给Job类加Component注解的方法。文档的其他篇幅就介绍了一些配置但是介绍得也不全面看了帮助也并不是很大。详细的配置可以参考w3school的 Quartz配置 。4 SpringBoot集成Quartz4.1 建表我选择将定时任务的信息保存在数据库中优点是显而易见的定时任务不会因为系统的崩溃而丢失。建表的sql语句在Quartz的github中可以找到里面有针对每一种常用数据库的sql语句具体地址是 Quartz数据库建表sql 。建表以后可以看到数据库里多了11张表。我们完全不需要关心每张表的具体作用在添加删除任务、触发器等的时候Quartz框架会操作这些表。4.2 引入依赖在 pom.xml 里添加依赖。!-- quartz 定时任务 --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-quartz/artifactIdversion2.2.6.RELEASE/version
/dependency4.3 配置quartz在 application.yml 中配置quartz。相关配置的作用已经写在注解上。# spring的datasource等配置未贴出
spring:quartz:# 将任务等保存化到数据库job-store-type: jdbc# 程序结束时会等待quartz相关的内容结束wait-for-jobs-to-complete-on-shutdown: true# QuartzScheduler启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录overwrite-existing-jobs: true# 这里居然是个map搞得智能提示都没有佛了properties:org:quartz:# scheduler相关scheduler:# scheduler的实例名instanceName: schedulerinstanceId: AUTO# 持久化相关jobStore:class: org.quartz.impl.jdbcjobstore.JobStoreTXdriverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate# 表示数据库中相关表是QRTZ_开头的tablePrefix: QRTZ_useProperties: false# 线程池相关threadPool:class: org.quartz.simpl.SimpleThreadPool# 线程数threadCount: 10# 线程优先级threadPriority: 5threadsInheritContextClassLoaderOfInitializingThread: true4.4 注册周期性的定时任务第1节中提到的第一个子需求是在每天0点执行的是一个周期性的任务任务内容也是确定的所以直接在代码里注册JobDetail和Trigger的bean就可以了。当然这些JobDetail和Trigger也是会被持久化到数据库里。/*** Quartz的相关配置注册JobDetail和Trigger* 注意JobDetail和Trigger是org.quartz包下的不是spring包下的不要导入错误*/
Configuration
public class QuartzConfig {Beanpublic JobDetail jobDetail() {JobDetail jobDetail JobBuilder.newJob(StartOfDayJob.class).withIdentity(start_of_day, start_of_day).storeDurably().build();return jobDetail;}Beanpublic Trigger trigger() {Trigger trigger TriggerBuilder.newTrigger().forJob(jobDetail()).withIdentity(start_of_day, start_of_day).startNow()// 每天0点执行.withSchedule(CronScheduleBuilder.cronSchedule(0 0 0 * * ?)).build();return trigger;}
}builder类创建了一个JobDetail和一个Trigger并注册成为Spring bean。从第3节中摘录的官方文档中我们已经知道这些bean会自动关联到调度器上。需要注意的是JobDetail和Trigger需要设置组名和自己的名字用来作为唯一标识。当然JobDetail和Trigger的唯一标识可以相同因为他们是不同的类。Trigger通过cron表达式指定了任务执行的周期。对cron表达式不熟悉的同学可以百度学习一下。JobDetail里有一个StartOfDayJob类这个类就是Job接口的一个实现类里面定义了任务的具体内容看一下代码Component
public class StartOfDayJob extends QuartzJobBean {private StudentService studentService;Autowiredpublic StartOfDayJob(StudentService studentService) {this.studentService studentService;}Overrideprotected void executeInternal(JobExecutionContext jobExecutionContext)throws JobExecutionException {// 任务的具体逻辑}
}网上很多博客也是这么介绍的。但是根据我的实际测试这样写可以完成依赖注入但我还不知道它的实现原理。4.5 注册无周期性的定时任务第1节中提到的第二个子需求是学生请假显然请假是不定时的一次性的而且不具有周期性。4.5节与4.4节大体相同但是有两点区别Job类需要获取到一些数据用于任务的执行任务执行完成后删除Job和Trigger。业务逻辑是在老师批准学生的请假申请时向调度器添加Trigger和JobDetail。实体类public class LeaveApplication {TableId(type IdType.AUTO)private Integer id;private Long proposerUsername;JsonFormat( pattern yyyy-MM-dd HH:mm,timezoneGMT8)private LocalDateTime startTime;JsonFormat( pattern yyyy-MM-dd HH:mm,timezoneGMT8)private LocalDateTime endTime;private String reason;private String state;private String disapprovedReason;private Long checkerUsername;private LocalDateTime checkTime;// 省略getter、setter
}Service层逻辑重要的地方已在注释中说明。Service
public class LeaveApplicationServiceImpl implements LeaveApplicationService {Autowiredprivate Scheduler scheduler;// 省略其他方法与其他依赖/*** 添加job和trigger到scheduler*/private void addJobAndTrigger(LeaveApplication leaveApplication) {Long proposerUsername leaveApplication.getProposerUsername();// 创建请假开始JobLocalDateTime startTime leaveApplication.getStartTime();JobDetail startJobDetail JobBuilder.newJob(LeaveStartJob.class)// 指定任务组名和任务名.withIdentity(leaveApplication.getStartTime().toString(),proposerUsername _start)// 添加一些参数执行的时候用.usingJobData(username, proposerUsername).usingJobData(time, startTime.toString()).build();// 创建请假开始任务的触发器// 创建cron表达式指定任务执行的时间由于请假时间是确定的所以年月日时分秒都是确定的这也符合任务只执行一次的要求。String startCron String.format(%d %d %d %d %d ? %d,startTime.getSecond(),startTime.getMinute(),startTime.getHour(),startTime.getDayOfMonth(),startTime.getMonth().getValue(),startTime.getYear());CronTrigger startCronTrigger TriggerBuilder.newTrigger()// 指定触发器组名和触发器名.withIdentity(leaveApplication.getStartTime().toString(),proposerUsername _start).withSchedule(CronScheduleBuilder.cronSchedule(startCron)).build();// 将job和trigger添加到scheduler里try {scheduler.scheduleJob(startJobDetail, startCronTrigger);} catch (SchedulerException e) {e.printStackTrace();throw new CustomizedException(添加请假任务失败);}}
}Job类逻辑重要的地方已在注释中说明。Component
public class LeaveStartJob extends QuartzJobBean {private Scheduler scheduler;private SystemUserMapperPlus systemUserMapperPlus;Autowiredpublic LeaveStartJob(Scheduler scheduler,SystemUserMapperPlus systemUserMapperPlus) {this.scheduler scheduler;this.systemUserMapperPlus systemUserMapperPlus;}Overrideprotected void executeInternal(JobExecutionContext jobExecutionContext)throws JobExecutionException {Trigger trigger jobExecutionContext.getTrigger();JobDetail jobDetail jobExecutionContext.getJobDetail();JobDataMap jobDataMap jobDetail.getJobDataMap();// 将添加任务的时候存进去的数据拿出来long username jobDataMap.getLongValue(username);LocalDateTime time LocalDateTime.parse(jobDataMap.getString(time));// 编写任务的逻辑// 执行之后删除任务try {// 暂停触发器的计时scheduler.pauseTrigger(trigger.getKey());// 移除触发器中的任务scheduler.unscheduleJob(trigger.getKey());// 删除任务scheduler.deleteJob(jobDetail.getKey());} catch (SchedulerException e) {e.printStackTrace();}}
}5 总结上文所述的内容应该可以满足绝大部分定时任务的需求。我在查阅网上的博客之后发现大部分博客里介绍的Quartz使用还是停留在Spring阶段配置也都是通过xml因此我在实现了功能以后将整个过程总结了一下留给需要的人以及以后的自己做参考。总体上来说Quartz实现定时任务还是非常方便的与SpringBoot整合之后配置也非常简单是实现定时任务的不错的选择。5.2 小坑1在IDEA2020.1版本里使用SpringBoot与Quartz时报错找不到org.quartz程序包但是依赖里面明明有org.quartz类里的import也没有报错还可以通过Ctrl鼠标左键直接跳转到相应的类里。后面我用了IDEA2019.3.4就不再有这个错误。那么就是新版IDEA的BUG了。本文由博客群发一文多发等运营工具平台 OpenWrite 发布来源https://www.tuicool.com/articles/zyQfmqV