作者简介,普修罗双战士,一直追求不断学习和成长,在技术的道路上持续探索和实践。
多年互联网行业从业经验,历任核心研发工程师,项目技术负责人。
欢迎 点赞✍评论⭐收藏
SpringBoot 领域知识
链接 | 专栏 |
---|---|
SpringBoot 专业知识学习一 | SpringBoot专栏 |
SpringBoot 专业知识学习二 | SpringBoot专栏 |
SpringBoot 专业知识学习三 | SpringBoot专栏 |
SpringBoot 专业知识学习四 | SpringBoot专栏 |
SpringBoot 专业知识学习五 | SpringBoot专栏 |
SpringBoot 专业知识学习六 | SpringBoot专栏 |
SpringBoot 专业知识学习七 | SpringBoot专栏 |
SpringBoot 专业知识学习八 | SpringBoot专栏 |
SpringBoot 专业知识学习九 | SpringBoot专栏 |
SpringBoot 专业知识学习十 | SpringBoot专栏 |
本文将详细介绍如何在 Spring Boot 中实现定时任务,并涵盖了控制层、服务层、数据访问层、XML和YAML配置的使用方式。我们将从数据库创建表开始,一步一步地实现这些功能,并提供注意事项。最后,我们将总结整个过程。
为了实现定时任务的功能,首先需要创建一个数据库表。我们可以创建一个名为 task
的表,包含以下四个字段:
创建执行定时任务的数据库表可以按照以下 SQL 语句来进行:
CREATE TABLE `task` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`dueDate` datetime DEFAULT NULL,
`completed` boolean DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
该表包含了 id
、name
、description
、 dueDate
和 completed
五个字段。其中,id
为自增主键,name
表示定时任务的名称,description
表示定时任务描述,dueDate
表示定时任务结束执行的时间,completed
表示定时任务是否完成。可以根据实际需求修改表结构。
在 pom.xml
文件中添加定时任务依赖 spring-boot-starter-quartz
:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>demoartifactId>
<version>1.0.0version>
<properties>
<java.version>11java.version>
<spring.boot.version>2.6.1spring.boot.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>${spring.boot.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<version>${spring.boot.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
<version>${spring.boot.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-quartzartifactId>
<version>${spring.boot.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.27version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.1version>
<configuration>
<source>${java.version}source>
<target>${java.version}target>
configuration>
plugin>
plugins>
build>
project>
这样我们就可以使用 Quartz 框架来实现定时任务。
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.time.LocalDateTime;
@Entity
public class Task {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
private LocalDateTime dueDate;
private boolean completed;
// 构造函数、Getter 和 Setter 方法
public Task() {
}
public Task(String name, String description, LocalDateTime dueDate) {
this.name = name;
this.description = description;
this.dueDate = dueDate;
this.completed = false;
}
// Getter 和 Setter 方法省略...
// 其他属性和方法省略...
}
在这个示例中,Task
实体类有以下属性:
id
:任务的唯一标识符,使用自动生成的递增值。name
:任务的名称。description
:任务的描述。dueDate
:任务的截止日期。completed
:任务是否已完成的标志。注意,在这个示例中,使用了 Java 8 的 LocalDateTime
类型来表示日期和时间。您可以根据具体需求选择适合的日期时间类型。
请根据您的具体需求修改属性、构造函数和方法。接下来,您可以创建一个与数据库进行交互的 DAO 层。
定义对定时任务的基本增删改查方法,如 addTask
、deleteTask
、updateTask
、findTaskById
等。
import java.time.LocalDateTime;
import java.util.List;
public interface TaskDao {
void addTask(Task task);
void deleteTask(long taskId);
void updateTask(long taskId, String name, String description, LocalDateTime dueDate, boolean completed);
Task findTaskById(long taskId);
List<Task> findTasks();
}
在这个示例中,TaskDao
定义了如下基本操作:
addTask
:添加一个新的任务。deleteTask
:删除指定的任务。updateTask
:更新任务的名称、描述、截止日期和完成状态。findTaskById
:根据任务 ID 查找任务。findTasks
:获取当前所有的任务列表。可以根据具体需求添加、修改或删除方法。但是,通常来说,一个 DAO 接口需要定义基本的 CRUD 操作,即增加、删除、更新、查询。我们建议您采用命名规范,例如按照方法名构造 SQL 语句,或者使用注解进行映射。另外,为了管理和维护代码,我们建议您将实现代码放在另外的类中,例如命名为 TaskDaoImpl
。
TaskService
类中实现定时任务的操作方法,包括添加、删除、修改和查询方法等。TaskDao
中的方法来完成对数据库的操作,如创建新任务、删除任务、修改任务等。@Autowired
注解将 TaskDao
注入到 TaskService
中,方便调用。import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class TaskService {
private final TaskDao taskDao;
@Autowired
public TaskService(TaskDao taskDao) {
this.taskDao = taskDao;
}
public void addTask(String name, String description, LocalDateTime dueDate) {
Task task = new Task(name, description, dueDate);
taskDao.addTask(task);
}
public void deleteTask(long taskId) {
taskDao.deleteTask(taskId);
}
public void updateTask(long taskId, String name, String description, LocalDateTime dueDate, boolean completed) {
Task existingTask = taskDao.findTaskById(taskId);
if (existingTask != null) {
existingTask.setName(name);
existingTask.setDescription(description);
existingTask.setDueDate(dueDate);
existingTask.setCompleted(completed);
taskDao.updateTask(existingTask);
} else {
throw new IllegalArgumentException("Task not found with ID: " + taskId);
}
}
public Task findTaskById(long taskId) {
return taskDao.findTaskById(taskId);
}
public List<Task> findTasks() {
return taskDao.findTasks();
}
}
在这个示例中,TaskService
类使用了依赖注入将 TaskDao
对象注入进来,并实现了以下操作方法:
addTask
:创建一个新的任务,并调用 TaskDao
的 addTask
方法将其添加到数据库中。deleteTask
:删除指定 ID 的任务,并调用 TaskDao
的 deleteTask
方法进行删除操作。updateTask
:根据指定 ID 更新任务的属性,并调用 TaskDao
的 updateTask
方法进行更新操作。findTaskById
:根据指定 ID 查找并返回任务对象。findTasks
:获取当前所有的任务列表。请根据您的具体需求修改代码,并确保将适当的错误处理和验证逻辑添加到每个方法中。此外,确保适当地处理依赖注入,以使 TaskDao
正确地注入到 TaskService
类中。
请注意,在这个示例中,使用了 Spring 的 @Service
和 @Autowired
注解来实现依赖注入和服务的声明。如果您没有使用 Spring 或其他类似的框架,您可以手动创建和管理相关对象的实例。
TaskController
类中定义定时任务的 API 接口,使用 RESTful 风格。@Autowired
注解将 TaskService
注入到 TaskController
中,方便调用 TaskService
中的方法。当您在 Controller 层中创建的时候,可以考虑如下代码示例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.List;
@RestController
@RequestMapping("/tasks")
public class TaskController {
private final TaskService taskService;
@Autowired
public TaskController(TaskService taskService) {
this.taskService = taskService;
}
@PostMapping
public ResponseEntity<Task> addTask(@RequestBody TaskRequest taskRequest) {
taskService.addTask(taskRequest.getName(), taskRequest.getDescription(), taskRequest.getDueDate());
return new ResponseEntity<>(HttpStatus.CREATED);
}
@DeleteMapping("/{taskId}")
public ResponseEntity<Task> deleteTask(@PathVariable long taskId) {
taskService.deleteTask(taskId);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@PutMapping("/{taskId}")
public ResponseEntity<Task> updateTask(@PathVariable long taskId, @RequestBody TaskRequest taskRequest) {
taskService.updateTask(taskId, taskRequest.getName(), taskRequest.getDescription(), taskRequest.getDueDate(), taskRequest.isCompleted());
return new ResponseEntity<>(HttpStatus.OK);
}
@GetMapping("/{taskId}")
public ResponseEntity<Task> findTaskById(@PathVariable long taskId) {
Task task = taskService.findTaskById(taskId);
if (task != null) {
return new ResponseEntity<>(task, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
@GetMapping
public ResponseEntity<List<Task>> findTasks() {
List<Task> tasks = taskService.findTasks();
return new ResponseEntity<>(tasks, HttpStatus.OK);
}
}
在这个示例中,TaskController
类使用了 Spring 的 MVC 注解来定义不同的 HTTP 请求处理方法:
@RestController
:声明这是一个 RESTful 控制器。@RequestMapping("/tasks")
:指定该控制器处理的 URL 前缀。@PostMapping
:处理 HTTP POST 请求,并调用 addTask
方法创建任务。@DeleteMapping("/{taskId}")
:处理 HTTP DELETE 请求,并调用 deleteTask
方法删除指定 ID 的任务。@PutMapping("/{taskId}")
:处理 HTTP PUT 请求,并调用 updateTask
方法更新指定 ID 的任务。@GetMapping("/{taskId}")
:处理 HTTP GET 请求,并调用 findTaskById
方法获取指定 ID 的任务。@GetMapping
:处理 HTTP GET 请求,并调用 findTasks
方法获取所有任务列表。请根据您的具体需求修改代码,并确保将适当的错误处理和验证逻辑添加到每个方法中。此外,确保适当地处理依赖注入,以使 TaskService
正确地注入到 TaskController
类中。
在这个示例中,TaskRequest
是一个用于接收客户端请求的数据传输对象(DTO),根据您的需求,您可以根据实际情况创建适合自己的 DTO 类来接收和传递数据。
请注意,在这个示例中,使用了 Spring 的注解来简化了 RESTful API 的开发,如果您不使用 Spring 框架,可以根据自己所用框架的要求来编写 Controller 层的代码。
创建一个 QuartzUtil
工具类,用于创建定时任务实例。
使用 JobBuilder
、TriggerBuilder
等工具类,设置定时任务的属性,如任务名称、任务组名、任务类、触发器类型、触发器名称、触发器组名等。
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
public class QuartzUtil {
private static Scheduler scheduler;
static {
try {
scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
public static void addJob(String jobId, String jobGroup, String triggerId, String triggerGroup, Class<? extends Job> jobClass, String cronExpression) throws SchedulerException {
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobId, jobGroup).build();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerId, triggerGroup).withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)).build();
scheduler.scheduleJob(jobDetail, trigger);
}
public static void deleteJob(String jobId, String jobGroup) throws SchedulerException {
JobKey jobKey = new JobKey(jobId, jobGroup);
scheduler.deleteJob(jobKey);
}
public static void pauseJob(String jobId, String jobGroup) throws SchedulerException {
JobKey jobKey = new JobKey(jobId, jobGroup);
scheduler.pauseJob(jobKey);
}
public static void resumeJob(String jobId, String jobGroup) throws SchedulerException {
JobKey jobKey = new JobKey(jobId, jobGroup);
scheduler.resumeJob(jobKey);
}
public static void shutdown() throws SchedulerException {
scheduler.shutdown();
}
}
使用示例:
public class TestJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("执行定时任务...");
}
}
public class Main {
public static void main(String[] args) {
try {
// 添加一个每分钟执行一次的定时任务
QuartzUtil.addJob("job1", "jobGroup", "trigger1", "triggerGroup", TestJob.class, "0 * * * * ?");
Thread.sleep(10000);
// 暂停定时任务
QuartzUtil.pauseJob("job1", "jobGroup");
Thread.sleep(10000);
// 恢复定时任务
QuartzUtil.resumeJob("job1", "jobGroup");
Thread.sleep(10000);
// 删除定时任务
QuartzUtil.deleteJob("job1", "jobGroup");
// 停止定时任务调度器
QuartzUtil.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
}
需要注意的是,上述示例仅为基础示例,具体使用中还需要根据实际情况进行调整。定时任务的配置可以参考 Quartz 官方文档,这里不再赘述。
TaskJob
类,实现 Quartz
框架的 Job
接口,实现需要执行的任务逻辑。TaskJob
类中,通过重写 execute
方法来实现具体的任务逻辑。可以在 TaskJob
类中重写 execute
方法来实现具体的任务逻辑。以下是一个示例:
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class TaskJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
// 获取任务参数
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String taskName = dataMap.getString("taskName");
// 需要执行的任务逻辑
System.out.println("执行任务:" + taskName);
// 任务完成后的操作
System.out.println("任务执行完成");
}
}
在上述示例中,我们通过 JobDataMap
获取了任务的参数,例如 taskName
。在 execute
方法中可以编写任意复杂的任务逻辑,您可以根据实际需求进行相应的操作,例如发送电子邮件、生成报告等。最后,您还可以在任务执行完成后进行适当的清理操作或记录完成状态。请根据您的具体需求在 execute
方法中编写任务逻辑。
在 application.yml
文件中添加定时任务的配置:
spring:
quartz:
job-store-type: jdbc
jdbc:
initialize-schema: always
这样就可以从数据库中读取定时任务的配置信息,并在启动时创建相应的数据库表。
在启动 Spring Boot 项目后,通过浏览器访问 http://localhost:8080/task
来测试新增定时任务的功能是否生效。
通过以上步骤,我们成功地在 Spring Boot 中实现了定时任务的功能。使用 Spring Boot Starter Quartz 提供的依赖,我们可以方便地创建和管理定时任务。通过创建数据库表、编写实体类、DAO 层、Service 层和 Controller 层,我们实现了定时任务的增删改查功能。
需要注意的是,定时任务的时间格式是 cron
表达式,需要熟悉 cron
表达式的语法规则。同时,在测试时要注意定时任务是否已经失效,否则会影响测试结果。
总的来说,Spring Boot 提供了简单、便捷的方式来实现定时任务。通过合理地使用 Spring Boot 中的各个组件,我们可以快速地开发定时任务功能,并为我们的项目提供定时任务的支持。