定时任务在很多场景有用,比如定时监控某服务,定时处理某处理等等,那么SpringBoot里面这么搞定时任务呢?下面我们来看看SpringBoot 定时任务的几种方式。
1.基于注解(最简单明了的一种)
来看看怎么注解,我们在service层定义个定时任务类吧,就叫ServiceJob:
@Component
@EnableScheduling //开启定时任务
public class ServiceJob {
// 3秒执行一次
@Scheduled(cron = "0/3 * * * * ?")
public void runJob1() {
System.out.println("runJob1--"+new Date());
}
}
可以看到控制台每三秒打印一次,这就是最简单的注解定时任务了。Cron表达式参数分别表示如下:
@Scheduled:除了支持灵活的参数表达式cron之外,还支持简单的延时操作,例如 fixedDelay ,fixedRate 填写相应的毫秒数即可。如 Scheduled(fixedDelay = 3000 或fixedRate =3000)。注解的方式的确是太简单了,但是有个缺点,发现没有,如果我们每次要修改定时任务执行的频率,是不是要改注解里面的值,然后再重启服务。非常的不灵活。那怎么搞呢?解决办法就是基于接口。
2.基于数据库配置的定时任务
2.1、既然用到数据库,那我们就把数据库的依赖引入,如下:
- 秒(0~59) 例如0/5表示每5秒
- 分(0~59)
- 时(0~23)
- 日(0~31)的某天,需计算
- 月(0~11)
- 周几( 可填1-7 或 SUN/MON/TUE/WED/THU/FRI/SAT)
<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>
2.2、创建DynamicServiceJob如下:
@Component
@EnableScheduling
public class DynamicServiceJob implements SchedulingConfigurer{
// 注入mapper
@Autowired
CronMapper cronMapper;
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(
// 1.添加任务内容
() -> System.out.println("DynamicServiceJob执行定时任务"+ new Date())
,
// 2设置执行周期
triggerContext -> {
// 2.1 从数据库获取执行周期
String corn = cronMapper.getCorn();
// 2.2校验 corn是否合法
if(StringUtils.isEmpty(corn)) {
System.out.println("配置的定时任务频率为空!");
}
// 2.3 返回执行周期
return new CronTrigger(corn).nextExecutionTime(triggerContext);
}
);
}
}
2.3、创建我们的mapper:
@Mapper
public interface CronMapper {
@Select("select cron from cron where cron_id = 1")
public String getCorn();
}
2.4 、application.yml配置数据库:
spring:
datasource:
url: jdbc:mysql://localhost:3306/scheduled?serverTimezone=GMT%2B8
username: root
2.5、创建我们的数据库表并插入数据:
CREATE TABLE `cron` (
`cron_id` varchar(30) NOT NULL PRIMARY KEY,
`cron` varchar(30) NOT NULL
);
INSERT INTO `cron` VALUES ('1', '0/2 * * * * ?');
Query OK, 0 rows affected
Query OK, 1 row affected
2.6、跑起来看看
数据库改为5秒:
再来看看效果:
成功!这里需要注意的是如果在数据库修改时格式出现错误,则定时任务会停止。即使重新修改正确,也只能重新启动项目才能恢复。
3.异步定时任务
3.1、首先我们来看看非异步定时的效果,创建两定时任务,如下:
// 2秒执行一次
@Scheduled(cron = "0/2 * * * * ?")
public void runJob1() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "runJob1--" + new Date());
}
// 1秒执行一次
@Scheduled(cron = "0/1 * * * * ?")
public void runJob2() {
System.out.println(Thread.currentThread().getName() +"runJob2--" + new Date());
}
结果如下:
可以看到,定时任务都在一个线程里面,且相互之间有影响,那怎么解决呢?答案就是异步(也就是多线程),来看看怎么实现
3.2、异步定时任务
@Component
@EnableScheduling // 1.开启定时任务
@EnableAsync // 2.开启多线程
public class ServiceJob {
// 2秒执行一次
@Async
@Scheduled(cron = "0/2 * * * * ?")
public void runJob1() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "runJob1--" + new Date());
}
// 1秒执行一次
@Async
@Scheduled(cron = "0/1 * * * * ?")
public void runJob2() {
System.out.println(Thread.currentThread().getName() +"runJob2--" + new Date());
}
}
来看看执行效果:
可以看到,定时任务在不同的线程中执行,且相互间不影响。