使用SpringBoot创建定时任务非常简单,目前主要有以下创建方式:
一、基于注解(@Scheduled)
二、基于接口(SchedulingConfigurer) 前者相信大家都很熟悉,但是实际使用中我们往往想从数据库中读取指定时间来动态执行定时任务,这时候基于接口的定时任务就派上用场了。
一、静态:基于注解
基于注解 @Scheduled 默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影响。
1、创建定时器
使用SpringBoot基于注解来创建定时任务非常简单,只需几行代码便可完成。 代码如下:
@Configuration //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling // 2.开启定时任务
public class SaticScheduleTask {
//3.添加定时任务
@Scheduled(cron = "0/5 * * * * ?")
//或直接指定时间间隔,例如:5秒
//@Scheduled(fixedRate=5000)
private void configureTasks() {
System.err.println("执行静态定时任务时间: " + LocalDateTime.now());
}
}
Cron表达式参数分别表示:
秒(0~59) 例如0/5表示每5秒
分(0~59)
时(0~23)
日(0~31)的某天,需计算
月(0~11)
周几( 可填1-7 或 SUN/MON/TUE/WED/THU/FRI/SAT)
@Scheduled:除了支持灵活的参数表达式cron之外,还支持简单的延时操作,例如 fixedDelay ,fixedRate 填写相应的毫秒数即可。
2、启动测试
启动应用,可以看到控制台打印出如下信息:
显然,使用@Scheduled 注解很方便,但缺点是当我们调整了执行周期的时候,需要重启应用才能生效,这多少有些不方便。为了达到实时生效的效果,可以使用接口来完成定时任务。
二、动态:基于接口
基于接口(SchedulingConfigurer)
1、导入pom依赖包:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.4.RELEASE</version>
</parent>
<dependencies>
<dependency><!--添加Web依赖 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><!--添加MySql依赖 -->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency><!--添加Mybatis依赖 配置mybatis的一些初始化的东西-->
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<dependency><!-- 添加mybatis依赖 -->
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
<scope>compile</scope>
</dependency>
</dependencies>
2、创建定时器
数据库准备好数据之后,我们编写定时任务,注意这里添加的是TriggerTask,目的是循环读取我们在数据库设置好的执行周期,以及执行相关定时任务的内容。
具体代码如下:
(1) 启动类中使用 @EnableScheduling 注解, 开启定时任务
@SpringBootApplication
@EnableScheduling // 定时任务
public class AskApplication {
private static final Logger logger = LoggerFactory.getLogger(AskApplication.class);
public static void main(String[] args) {
SpringApplication.run(AskApplication.class, args);
logger.info("========================启动完毕========================");
}
}
(2) 类实现 SchedulingConfigurer 编写定时器
package com.jz.ask.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jz.ask.Thread.InsertRunnable;
import com.jz.ask.common.utils.cron.GetScheduleTimeOfCron;
import freemarker.template.utility.NormalizeNewlines;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author zhaorenhui
* @Date 2020/10/10
* TODO 定时拉取数据 存储到数据库中 , 可动态修改cron定时时间
*/
@Service
public class ScheduledTaskService implements SchedulingConfigurer {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 默认每隔 15秒执行一次 秒 分 时 天 月 周
private static final String DEFAULT_CRON = "0 53 * * * ?";
private String cron = DEFAULT_CRON;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(() -> {
System.out.println("执行定时任务");
}, (triggerContext) -> { //触发器
// 定时任务触发,可修改定时任务的执行周期
CronTrigger trigger = new CronTrigger(cron);
Date nextExecDate = trigger.nextExecutionTime(triggerContext);
System.out.println(nextExecDate);
return nextExecDate;
});
}
/**
* 通过时间秒毫秒数判断两个时间的间隔(小时)
* @param date1
* @param date2
* @return
*/
public int differenceBetween(Date date1,Date date2)
{
int days = (int) ((date2.getTime() - date1.getTime()) / (1000*3600));
return days;
}
// 将集合 按照指定大小进行切分
public List<List<Shop>> splitList(List<Shop> list , Integer size) {
List<List<Shop>> shopList = new ArrayList<>();
final int N = list.size();
for (int i = 0; i < N; i += size) {
shopList.add(new ArrayList<Shop>(
list.subList(i, Math.min(N, i + size)))
);
}
return shopList;
}
}
3、启动测试
启动应用后,任务会定时执行