SpringBoot实现用户定制的定时任务(动态定时任务)
文章目录
情景
我们知道SpringBoot能使用@Scheduled注解来进行定时任务的控制,该注解需要配合Cron表达式以及在启动类上添加@EnableScheduling注解才能使用。
不过我们现在的假定情景并不是程序员设定的定时任务,而是用户可以在我们的网页上定制定时任务,前端将该任务的信息发送到后端后,后端可以将此任务存入数据库并在规定的时间内执行。例如用户可以设定定时任务的执行时间段,执行时刻等,并可以随时新增、删除和改变定时任务。
接下来我们来使用SpringBoot实现这个假定情景
实现
实体类Cron
我们需要创建实体类Cron代表定时任务,这里假设Cron有如下属性:执行时刻、任务标题、任务开始的日期、任务截止日期,以及存入数据库所需要的几个基本属性:id(作为主键)、创建时间、更新时间、状态status
我们用一个BaseEntity来保存基本属性,Cron将继承BaseEntity,使用MyBatisPlus作为ORM框架,Cron的代码如下:
@Data
@EqualsAndHashCode(callSuper = true)
public class Cron extends BaseEntity {
private static final long serialVersionUID = 1L;
@NotNull(message = "执行时刻不能为空")
private LocalTime executeTime;
@NotBlank(message = "标题不能为空")
private String title;
@NotNull(message = "截止日期不能为空")
private LocalDate deadTime;
@NotNull(message = "开始日期不能为空")
private LocalDate startTime;
}
这里需要注意的是lombok的@Data注解相当于@Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode这5个注解的合集。
其中,@EqualsAndHashCode注解会生成equals(Object other) 和 hashCode()方法。我们重写了equals(Object other) 和 hashCode()方法,就是为了在两个对象的属性相同时equals能返回true,认为它们两个相同。但@EqualsAndHashCode默认仅使用该类中定义的属性且不会调用父类的equals(Object other) 和 hashCode()方法。这是什么意思呢?仅使用该类中的属性,也就是如果该类的两个对象属性相同,即使这两个对象对应父类的属性不同,equals也会认为它们两个对象相同,从而返回true。默认的实现中不使用父类的属性,将会导致问题,比如,有多个类有相同的部分属性,恰好id(数据库主键)在父类中,那么就会存在部分对象在比较时,它们并不相等,却因为lombok自动生成的equals(Object other) 和 hashCode()方法判定为相等,从而导致出错。所以我们在使用@Data时同时需要加上@EqualsAndHashCode(callSuper=true)注解来解决这一问题,加上(callSuper=true),其生成的equals(Object other) 和 hashCode()方法将调用父类的方法,也就是会考虑父类的属性。
加上@EqualsAndHashCode(callSuper=true)就符合我们的要求了,这样即使两个Cron对象,它们的属性相同,但它们在父类中对应的主键不同,equals将认为它们是不同的对象,返回false。
对于前端传参,我们需要进行非空验证,我们在实体类中还加入了@NotNull和@NotBlank注解,并且使用message配置提示语句。这两个注解都来自于javax.validation.constraints包,该包内还有另一个常用注解@NotEmpty,@NotEmpty 用在集合上面,一般用来校验List类型(不能注释枚举类型),而且长度必须大于0。@NotBlank 用在String上面,一般用来校验String类型不能为空,而且调用trim()后,长度必须大于0。@NotNull 可用在所有类型上,校验是否为非null。这些注解都需要配合@Validated注解使用,从而检验Controller的入参是否符合规范,例如:
public Result save(@Validated @RequestBody Cron cron)
Cron的父类BaseEntity的代码如下:
@Data
public class BaseEntity implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private LocalDateTime created;
private LocalDateTime updated;
private Integer status;
}
由于实体类需要在网络中传输,所以BaseEntity需要实现Serializable接口,这里使用MyBatisPlus的@TableId注解进行属性与数据库主键的映射。
Service层:接口CronService以及其实现类CronServiceImpl
我们需定义接口CronService来实现用户定制定时任务需求。用户能创建、删除、修改定时任务,创建任务即判断当前日期是否为既定的执行日期,若是则启动定时任务。删除任务即判断该任务是否已被启动,若是,则将其停止。修改任务即先停止该任务,再重新启动该任务。我们让CronService继承MyBatisPlus的IService接口,对应的数据库操作直接在Controller层中调用相应方法即可,我们就不需要再在CronService中定义了。于是,我们需要在CronService中定义startCron(Cron cron)
、stopCron(Cron cron)
、changeCron(Cron cron)
三个方法,分别对应用户的创建、删除、修改定时任务操作。
public interface CronService extends