Spring Boot 中实现定时任务(quartz)功能实战

发布时间:2024年01月16日

在这里插入图片描述

🏆作者简介,普修罗双战士,一直追求不断学习和成长,在技术的道路上持续探索和实践。
🏆多年互联网行业从业经验,历任核心研发工程师,项目技术负责人。
🎉欢迎 👍点赞?评论?收藏

🔎 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配置的使用方式。我们将从数据库创建表开始,一步一步地实现这些功能,并提供注意事项。最后,我们将总结整个过程。

在 Spring Boot 中实现定时任务功能


1. 创建数据库表

为了实现定时任务的功能,首先需要创建一个数据库表。我们可以创建一个名为 task 的表,包含以下四个字段:

  • id:自增ID
  • name :定时任务的名称
  • description :定时任务描述
  • dueDate :定时任务结束执行的时间
  • completed:定时任务是否完成

创建执行定时任务的数据库表可以按照以下 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;

该表包含了 idnamedescriptiondueDatecompleted 五个字段。其中,id 为自增主键,name 表示定时任务的名称,description 表示定时任务描述,dueDate 表示定时任务结束执行的时间,completed 表示定时任务是否完成。可以根据实际需求修改表结构。


2. 添加依赖

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.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>11</java.version>
        <spring.boot.version>2.6.1</spring.boot.version>
    </properties>

    <dependencies>
        <!-- Spring Boot Starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>

        <!-- Spring Boot Starter Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>

        <!-- Spring Boot Starter Data JPA -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>

        <!-- Spring Boot Starter Quartz -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>

        <!-- MySQL Connector -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>

        <!-- Other dependencies -->
        <!-- ... -->
    </dependencies>

    <build>
        <plugins>
            <!-- Maven Compiler Plugin -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

这样我们就可以使用 Quartz 框架来实现定时任务。


3. 创建定时任务实体类和 DAO 层

3.1 首先创建一个Task实体类,用于表示定时任务的属性。
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 层。

3.2 创建一个 TaskDao 接口

定义对定时任务的基本增删改查方法,如 addTaskdeleteTaskupdateTaskfindTaskById 等。

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


4. 创建 Service 层

4.1 在 TaskService 类中实现定时任务的操作方法,包括添加、删除、修改和查询方法等。
4.2 在方法中,可以通过调用 TaskDao 中的方法来完成对数据库的操作,如创建新任务、删除任务、修改任务等。
4.3 可以使用 @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:创建一个新的任务,并调用 TaskDaoaddTask 方法将其添加到数据库中。
  • deleteTask:删除指定 ID 的任务,并调用 TaskDaodeleteTask 方法进行删除操作。
  • updateTask:根据指定 ID 更新任务的属性,并调用 TaskDaoupdateTask 方法进行更新操作。
  • findTaskById:根据指定 ID 查找并返回任务对象。
  • findTasks:获取当前所有的任务列表。

请根据您的具体需求修改代码,并确保将适当的错误处理和验证逻辑添加到每个方法中。此外,确保适当地处理依赖注入,以使 TaskDao 正确地注入到 TaskService 类中。

请注意,在这个示例中,使用了 Spring 的 @Service@Autowired 注解来实现依赖注入和服务的声明。如果您没有使用 Spring 或其他类似的框架,您可以手动创建和管理相关对象的实例。


5. 创建 Controller 层

5.1 在 TaskController 类中定义定时任务的 API 接口,使用 RESTful 风格。
5.2 可以使用 @Autowired 注解将 TaskService 注入到 TaskController 中,方便调用 TaskService 中的方法。
5.3 实现增删改查等控制器方法,通过 HTTP 请求来调用相应的方法。

当您在 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 层的代码。


6. 创建定时任务工具类

创建一个 QuartzUtil 工具类,用于创建定时任务实例。

使用 JobBuilderTriggerBuilder 等工具类,设置定时任务的属性,如任务名称、任务组名、任务类、触发器类型、触发器名称、触发器组名等。

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 官方文档,这里不再赘述。


7. 创建定时任务类
7.1 创建一个 TaskJob 类,实现 Quartz 框架的 Job 接口,实现需要执行的任务逻辑。
7.2 在 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 方法中编写任务逻辑。


8. 配置定时任务

application.yml 文件中添加定时任务的配置:

spring:
  quartz:
    job-store-type: jdbc
    jdbc:
      initialize-schema: always

这样就可以从数据库中读取定时任务的配置信息,并在启动时创建相应的数据库表。


9. 测试

在启动 Spring Boot 项目后,通过浏览器访问 http://localhost:8080/task 来测试新增定时任务的功能是否生效。


10. 实现总结

通过以上步骤,我们成功地在 Spring Boot 中实现了定时任务的功能。使用 Spring Boot Starter Quartz 提供的依赖,我们可以方便地创建和管理定时任务。通过创建数据库表、编写实体类、DAO 层、Service 层和 Controller 层,我们实现了定时任务的增删改查功能。

需要注意的是,定时任务的时间格式是 cron 表达式,需要熟悉 cron 表达式的语法规则。同时,在测试时要注意定时任务是否已经失效,否则会影响测试结果。

总的来说,Spring Boot 提供了简单、便捷的方式来实现定时任务。通过合理地使用 Spring Boot 中的各个组件,我们可以快速地开发定时任务功能,并为我们的项目提供定时任务的支持。

在这里插入图片描述

文章来源:https://blog.csdn.net/m0_50308467/article/details/135615848
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。