在日常的项目开发中,往往会涉及到一些需要做到定时执行的代码,例如自动将超过24小时的未付款的单改为取消状态,自动将超过14天客户未签收的订单改为已签收状态等等,那么为了在Spring Boot中实现此类需求我们需要使用定时任务。@Scheduled和quartz类都可以实现定时任务。
1.Scheduled
在Springboot启动类增加注解自动开启job。
@SpringBootApplication
@MapperScan(basePackages = {"com.hqyj.springboot.mapper"})
@EnableCaching //开启缓存支持
@EnableCanalClient
@EnableAsync
//表示启用Scheduled定时任务机制
@EnableScheduling
public class FirstdemoApplication {
public static void main(String[] args) {
SpringApplication.run(FirstdemoApplication.class, args);
}
}
编写定时器任务。
@Component
public class ScheduledTask {
//控制台每隔6秒钟会输出一次"hello,world!"
@Scheduled(cron ="*/6 * * * * ?")
public void sayHello() {
System.out.println("hello,world!");
}
}
@Scheduled除过cron还有三种方式:fixedRate,fixedDelay,initialDelay。
Corn表达式是一个字符串,字符串以5或6个空格隔开,分隔出6或7个时间域,每个时间域代表一个时间含义,它有两种语法。
语法一:Seconds Minutes Hours DayOfMonth Month DayOfWeek Year
语法二:Seconds Minutes Hours DayOfMonth Month DayOfWeek
SpringTask仅支持语法二,即它只能有6个时间域
cron表达式常用案例:

2.Scheduled接口实现
使用@Scheduled 注解很方便,但缺点是当我们调整了执行周期的时候,需要重启应用才能生效,这多少有些不方便。为了达到实时生效的效果,那么可以使用接口来完成定时任务,统一将定时器信息保存在数据库中。
实现步骤如下:
1).创建Scheduled实体类
@Entity
@Table(name="scheduled")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Scheduled extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@TableId(type = IdType.AUTO)
private Integer cronId; //定时器任务的ID
private String cronName; //定时器任务的名字
private String cron; //定时器任务的cron表达式
}
2).创建mapper层
public interface ScheduledMapper extends BaseMapper {
@Select("select cron from scheduled where cron_id = #{id}")
public String getCron(int id);
}
3).创建任务类。
@Component
public class ScheduledTask implements SchedulingConfigurer {
@Resource
protected ScheduledMapper scheduledMapper;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.addTriggerTask(() -> process(),
triggerContext -> {
String cron = scheduledMapper.getCron(1);
if (cron.isEmpty()) {
System.out.println("cron is null");
}
return new CronTrigger(cron).nextExecutionTime(triggerContext);
});
}
//这里写定时任务要做的事情...
private void process() {
System.out.println("hello,world!");
}
}
4).测试运行
在scheduled表里面,添加一个定时任务记录,如下图:

我们发现控制台,每隔10秒钟输出一次"hello,world!"。说明定时任务启动成功!我们可以在不重启应用的基础上,随意修改数据库里cron表达式从而达到修改定时任务的效果。
hello,world!
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5effd94] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@2099173083 wrapping com.mysql.cj.jdbc.ConnectionImpl@b6175e5] will not be managed by Spring
==> Preparing: select cron from scheduled where cron_id = ?
==> Parameters: 1(Integer)
<== Columns: cron
<== Row: */10 * * * * ?
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5effd94]
3.Quartz
Quartz是一个java编写的开源任务调度框架其主要调度元素有:
实例:
项目结构图如下:

Springboot+Quartz实现Mysql数据库定时备份。实现步骤如下:
1).pom.xml中添加依赖
<properties>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<version>${spring-boot.version}</version>
</dependency>
</dependencies>
2).编写JdbcUtils数据库工具类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.util.HashMap;
@Component
public class JdbcUtils {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
public HashMap<String, String> getDBInfo() {
String url = this.url;
String username = this.username;
String password = this.password;
String[] split = url.split(":");
String host = String.format("%s:%s:%s", split[0], split[1], split[2]);
String[] portSplit = split[3].split("/");
String port = portSplit[0];
String[] databaseSplit = portSplit[1].split("\\?");
String dbName = databaseSplit[0];
HashMap<String, String> result = new HashMap<>();
result.put("url",url);
result.put("host",host);
result.put("port",port);
result.put("dbName",dbName);
result.put("userName",username);
result.put("passWord",password);
return result;
}
}
3).编写Quartz接口
IQuartzService.java
public interface IQuartzService {
//执行数据库的备份任务
void mysqlBackupTask();
}
QuartzServiceImpl.java
import com.hqyj.springboot.service.IQuartzService;
import com.hqyj.springboot.util.JdbcUtils;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import java.sql.Date;
import java.time.LocalDate;
import java.io.File;
import java.util.Map;
@Slf4j
@Service
public class QuartzServiceImpl implements IQuartzService {
@Autowired
private JdbcUtils jdbcUtils;
@Override
public void mysqlBackupTask() {
String resourcePath = "E:/java2302_springboot_course/taskdemo/db_bak";
log.info("======执行定时器:定时备份数据库=======");
String backUpPath = resourcePath + "/sql/" + java.sql.Date.valueOf(LocalDate.now());
File backUpFile = new File(backUpPath);
if (!backUpFile.exists()) {
backUpFile.mkdirs();
}
File dataFile = new File(backUpPath + "/springboot" + System.currentTimeMillis() + ".sql");
//拼接cmd命令
StringBuffer sb = new StringBuffer();
Map<String, String> dbInfo = jdbcUtils.getDBInfo();
sb.append("mysqldump");
sb.append(" -u" + dbInfo.get("userName"));
sb.append(" -p" + dbInfo.get("passWord"));
sb.append(" " + dbInfo.get("dbName") + " > ");
sb.append(dataFile);
log.info("======数据库备份cmd命令为:" + sb.toString() + "=======");
try {
Process exec = Runtime.getRuntime().exec("cmd /c" + sb.toString());
if (exec.waitFor() == 0) {
log.info("======数据库备份成功,路径为:" + dataFile + "=======");
}
} catch (Exception e) {
log.info("======数据库备份失败,异常为:" + e.getMessage() + "=======");
}
}
}
4).编写Quartz任务类
import com.hqyj.springboot.service.IQuartzService;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class MysqlBackupTask extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//获取JobDetail中传递的参数
IQuartzService quartzService = (IQuartzService) jobExecutionContext.getJobDetail().getJobDataMap().get("quartzService");
quartzService.mysqlBackupTask();
}
}
5).注册Quartz定时任务
import com.hqyj.springboot.service.IQuartzService;
import com.hqyj.springboot.task.MysqlBackupTask;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MysqlBackupQuartzConfig {
private static final String MYSQL_BACKUP_JOB_GROUP_NAME = "MYSQL_BACKUP_JOB";
private static final String MYSQL_BACKUP_TRIGGER_GROUP_NAME = "MYSQL_BACKUP_TRIGGER";
@Autowired
private IQuartzService quartzService;
@Bean
public JobDetail mysqlBackupJobDetail() {
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("quartzService", quartzService);
JobDetail jobDetail = JobBuilder.newJob(MysqlBackupTask.class)
.withIdentity("mysqlBackupJobDetail", MYSQL_BACKUP_JOB_GROUP_NAME)
.usingJobData(jobDataMap)
.storeDurably()
.build();
return jobDetail;
}
@Bean
public Trigger mysqlBackupTriggerQuartz() {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0 58 13 * * ?");
Trigger trigger = TriggerBuilder.newTrigger()
.forJob(mysqlBackupJobDetail()) //关联上Test述的JobDetail
.withIdentity("mysqlBackupJobDetail", MYSQL_BACKUP_TRIGGER_GROUP_NAME) //给Trigger起个名字
.withSchedule(cronScheduleBuilder)
.build();
return trigger;
}
}
6).启动项目测试运行。