前言
创建定时任务方式以及区别:
- 基于注解
@Scheduled
-> 修改执行周期后需要重启应用才能生效 - 基于xml的方式
【@Configuration + @ImportResource + xml】
需要重启应用才能生效 - 基于接口
SchedulingConfigurer
-> 从数据库中读取指定时间来动态执行定时任务
项目结构:
一、基于注解 @Scheduled
/**
* <p>静态定时任务(基于注解)</p>
*
* @description : @Scheduled 除了支持灵活的参数表达式cron之外,还支持简单的延时操作,例如 fixedDelay ,fixedRate 填写相应的毫秒数即可。
* 缺点: 当我们调整了执行周期的时候,需要重启应用才能生效。 -> 为了达到实时生效的效果,可以使用接口来完成定时任务。
* @author suqinyi
* @Date 2022/1/10
* 注解方式: @EnableScheduling + @Scheduled(cron = "0/10 * * * * ?")
*/
@Configuration //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling // 2.开启定时任务
public class AppTask03_annotation {
/**
* 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 = "0/10 * * * * ?")
public void AppTask01() throws Exception {
System.out.println("注解方式的角度-10秒");
}
}
二、基于xml方式
xml的方式: @Configuration + @ImportResource + xml
01、配置类加载xml
/**
* 加载调度的配置文件
*/
@Configuration
//@ImportResource(locations={"classpath:appTask01.xml","appTask02.xml"})//加载调度xml
@ImportResource(locations={"classpath:appTask01.xml","appTask02.xml"})//加载调度xml
public class SpringTaskConfig {
}
02、xml调度文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!--声明一个具有一个线程的池,如果定义多个,每个对象将获取同样的运行机会-->
<task:scheduler id="sch" pool-size="10"/>
<!--任务的调度类-->
<bean id="testJob" class="sqy.springtaskdemo.task.AppTask01_xml"/>
<!--引用线程池-->
<task:scheduled-tasks scheduler="sch">
<!--调度任务 5秒-->
<task:scheduled ref="testJob" method="task01_xml" cron="0/5 * * * * ?"/>
</task:scheduled-tasks>
</beans>
03、任务的调度类
package sqy.springtaskdemo.task;
/**
* @author suqinyi
* @Date 2021/9/12
* xml的方式: @Configuration + @ImportResource + xml
*/
public class AppTask01_xml {
public void task01_xml() throws Exception {
System.out.println("01xml方式调度==>AppTask01_xml");
}
}
三、基于接口 SchedulingConfigurer
(数据库)
这里采用了mybatis去查下调度信息
数据库如图:
注明:这边只是简单的设计了id和时间-【应该还要有方法名、类路径、等】
(原因:好利用反射机制,执行方法-提高代码优雅性)
pom依赖
<!--
阿里druid连接池
1.1.23不能使用,大量ERROR问题:
discard long time none received connection
原因和方案:https://zhuanlan.zhihu.com/p/368683245
-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.22</version>
</dependency>
<!--mysql依赖8比5多了ssl-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
<!-- StringUtils工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
动态定时任务:
package sqy.springtaskdemo.task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import sqy.springtaskdemo.mapper.CronMapper;
import sqy.springtaskdemo.pojo.MyCronTask;
import java.util.Date;
import java.util.List;
/**
* 动态定时任务(基于接口 SchedulingConfigurer)
* 默认的,SchedulingConfigurer 使用的也是单线程的方式,如果需要配置多线程,则需要指定PoolSize
*
* @author suqinyi
* @Date 2022/1/10
*/
@Configuration
@EnableScheduling
public class DynamicTask implements SchedulingConfigurer {
@Autowired
CronMapper cronMapper;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
//获取全部的任务-这边简单话值设计了id和时间-【应该还要有方法名、类路径、等(好利用反射机制)】
List<MyCronTask> cronList = cronMapper.findAll();
System.out.println("任务总数:" + cronList.size());
/**
* scheduledTaskRegistrar -> 目的: 循环读取我们在数据库设置好的执行周期,以及执行相关定时任务的内容
* 1.添加任务内容(Runnable)
* 2.设置执行周期(Trigger)
* 2.1 从数据库获取执行周期
* 2.2 表示式合法性校验.
* 2.3 返回执行周期(Date)
*/
for (MyCronTask myCronTask : cronList) {
scheduledTaskRegistrar.addTriggerTask(
getRunnable(myCronTask.getCronId()),
triggerContext -> {
if (StringUtils.isEmpty(cronList.get(0).getCronTime())) {
System.out.println("表达式不合法");
}
Date date = new CronTrigger(myCronTask.getCronTime()).nextExecutionTime(triggerContext);
System.out.println("返回执行周期:" + date);
return date;
}
);
}
}
private Runnable getRunnable(Integer count) {
//这边推荐设计数据库存储方法名,根据反射来取出方法java
// 【例如存入一个为test01的字段,java写一个test01的方法。根据反射来创建出Runnable来执行方法】
if (count == 1) {
return new Runnable() {
@Override
public void run() {
System.out.println("数据库id为1===>任务1");
}
};
}else if (count == 2) {
return new Runnable() {
@Override
public void run() {
System.out.println("数据库id为2===>任务2");
}
};
}else {
return new Runnable() {
@Override
public void run() {
System.out.println("默认执行");
}
};
}
}
}