一 基础概念
创建定时任务非常简单,主要有两种创建方式:一、基于注解(@Scheduled) 二、基于接口(SchedulingConfigurer). 前者相信大家都很熟悉,但是实际使用中我们往往想从数据库中读取指定时间来动态执行定时任务,这时候基于接口的定时任务就大派用场了。
代码环境 : spring-boot-2.0.1.RELEASE
依赖文件:
<dependencies>
<!-- 上边引入 parent,因此 下边无需指定版本 -->
<!-- 包含 mvc,aop 等jar资源 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- <!–spring操作数据库jpa 用于将数据存入数据库的类和方法的集–>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-jpa</artifactId>-->
<!-- </dependency>-->
<!-- 热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>true</scope>
</dependency>
<!--spring模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--数据库相关-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- <dependency><!–添加Mybatis依赖 –>-->
<!-- <groupId>org.mybatis.spring.boot</groupId>-->
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!-- <version>1.3.1</version>-->
<!-- </dependency>-->
<!-- 做动态定时任务调度需要使用数据库 所以这边引入了mybatis-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
二. 静态自动执行任务
1.@Scheduled 除了支持灵活的参数表达式cron之外,还支持简单的延时操作,例如 fixedDelay ,fixedRate 填写相应的毫秒数即可。
2.需要手动加上Enable直接开启自动执行任务开关
优点:编码简单,容易立刻上手
缺点:对于定时调度时间要写死,不利于控制。 每个Schedule 只能执行一个定时任务
代码实现:
@Configuration
@EnableScheduling
public class ScheduleConfigForStatic {
@Scheduled(cron = "0/5 * * * * ?")
public void scheduleTask(){
System.out.println (1);
}
}
@Schedule(cron = "0 * * * * ?") 参数介绍。 上述代码,展示的效果是每间隔5s就会执行一次定时任务。
二、基于接口(SchedulingConfigurer). 前者相信大家都很熟悉,但是实际使用中我们往往想从数据库中读取指定时间来动态执行定时任务,这时候基于接口的定时任务就大派用场了。
首先我们去看下 SchedulingConfigurer 接口
@FunctionalInterface
public interface SchedulingConfigurer {
void configureTasks(ScheduledTaskRegistrar var1);
}
这个是jdk1.8提出了的函数式编程,所以我们实现这个接口,并且使用lamda表达式来编写内部函数的逻辑
动态获取时间需要依赖数据库,这里使用的是mysql 数据库,首先要在pom文件里加入mybatis 和 mysql 的驱动 的依赖,上面的依赖也有包含,请自己核对下pom文件
<!--数据库相关-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency><!--添加Mybatis依赖 -->
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
application.yml文件配置:
server:
port: 9001
spring:
application:
name: springScheduleDemo
#数据库配置
datasource:
url: jdbc:mysql://192.168.232.143:3306/mysql?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
实现代码如下:
@Configuration
@EnableScheduling
public class ScheduleForMysql implements SchedulingConfigurer {
@Mapper
public interface CronMapper{
@Select ("select cron from contable a where a.id = #{id,jdbcType=INTEGER}")
String getCron(int id);
}
@Autowired
@SuppressWarnings ("all")
private CronMapper cronMapper;
@Override
public void configureTasks (ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.addTriggerTask (
() -> System.out.println ("定时任务开始:" + LocalDateTime.now ().toLocalTime ()),
triggerContext -> {
String cron = cronMapper.getCron (1);
if (StringUtils.isEmpty (cron)) {
return null;
} else {
return new CronTrigger (cron).nextExecutionTime (triggerContext);
}
}
);
}
}
/**这里研究一下为什么 configureTasks 里面要这么写
SchedulingConfigurer 我们可以看到这个接口需要传入一个 ScheduledTaskRegistrar 对象
接下来我们可以找到 ScheduledTaskRegistrar 对象的方法,发下只要一个空参构造方法。往下发现 addTriggerTask 方法返回一个ScheduledTaskRegistrar对象。所以我们这边考虑直接使用这个方法
public void addTriggerTask(Runnable task, Trigger trigger) {
this.addTriggerTask(new TriggerTask(task, trigger));
}
第一个参数是一个回调的执行任务,这边我们用来打印时间可以方便测试
第二个参数是一个触发器 trigger,仍然我们去找怎么获取这个 trigger 对象,发现他是一个接口,所以我们需要看 对应的实现类 CronTrigger。
public interface Trigger {
@Nullable
Date nextExecutionTime(TriggerContext var1);
}
可以通过 CronTrigger 的构造方法创建对象,然后将下次的执行时间传入
*/
数据库配置:
测试结果: