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

在这里插入图片描述

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

SpringBoot 领域知识

链接 专栏
SpringBoot 专业知识学习一 SpringBoot专栏
SpringBoot 专业知识学习二 SpringBoot专栏
SpringBoot 专业知识学习三 SpringBoot专栏
SpringBoot 专业知识学习四 SpringBoot专栏
SpringBoot 专业知识学习五 SpringBoot专栏
SpringBoot 专业知识学习六 SpringBoot专栏
SpringBoot 专业知识学习七 SpringBoot专栏
SpringBoot 专业知识学习八 SpringBoot专栏
SpringBoot 专业知识学习九 SpringBoot专栏
SpringBoot 专业知识学习十 SpringBoot专栏

Spring Boot 中实现定时任务(quartz)功能实战_第1张图片

本文将详细介绍如何在 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.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 框架来实现定时任务。


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 中的各个组件,我们可以快速地开发定时任务功能,并为我们的项目提供定时任务的支持。

Spring Boot 中实现定时任务(quartz)功能实战_第2张图片

你可能感兴趣的:(Java专栏,SpringBoot专栏,Java基础学习,spring,boot,redis,后端,spring,cloud,java,intellij-idea)