SpringBoot 定时任务详解

本文详细介绍了如何在SpringBoot中启用定时任务支持,创建带有@Scheduled注解的定时任务,以及如何配置定时任务线程池。包括fixedRate和cron表达式的使用以及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定时任务是指预先设置好时间周期,程序按照设定的时间周期、固定频率或特定的时间点来执行某些操作。定时任务常见于后端项目,常应用于在数据跑批、定时任务执行和邮件通知类任务中。现阶段常见的定时任务有两种,暂且分为静态定时任务和动态定时任务。

  • 静态:是指在指定时间或指定任务场景的情况下执行。
  • 动态:执行条件方便修改,常用于业务逻辑和外部配置的灵活变动。

定时任务的常见应用场景:数据备份、日志清理、邮件通知、定时刷新缓存。

一、静态定时:SpringBoot 使用 @Scheduled 示例

这部分是使用 cron 表达式完成定时任务。表达式中设定时间可以设定在 配置文件 中,使用 @value 注解进行获取。也可以存储到数据库中,通过固定接口对设定时间进行增删改查操作,可以达到动态使用定时的目的。

在Spring Boot中创建定时任务,通常使用@Scheduled注解,这是Spring框架提供的一个功能,允许你按照固定的频率(如每天、每小时、每分钟等)执行某个方法。

网上有很多 icon表达式生成器 ,可以直接搜索 定时任务icon表达式生成器 ,这里就不放链接了。

1、在入口类开启定时任务支持

SpringBoot 应用的启动类上,添加 @EnableScheduling 注解来开启定时任务的支持。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

2、创建定时任务

创建一个包含定时任务方法的类,并在该方法上使用@Scheduled注解来指定任务的执行频率。

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduledTasks {

	// 每5秒执行一次
	@Scheduled(fixedRate = 5000)
	public void doTaskEveryFiveSeconds() {
		System.out.println("任务每5秒执行一次:" + new Date());
	}

	// 每天的特定时间执行一次
	@Scheduled(cron = "0 0 12 * * ?") // 每天中午12点执行
	public void doTaskEveryDayAtNoon() {
		System.out.println("任务每天中午12点执行一次:" + new Date());
	}
}
  • fixedRate 属性用于指定任务执行的固定频率(以毫秒为单位)。
  • cron 属性则允许你使用CRON表达式来指定任务的执行时间。

这两个属性在实际的项目开发中都会做到配置文件中,便于修改。

3、配置定时任务线程池(可选)

默认情况下,Spring Boot使用单线程来执行所有的定时任务。如果需要并发执行多个定时任务,或者某个任务执行时间较长不希望阻塞其他任务,可以自定义定时任务的线程池。

application.properties 中设置相关配置

spring.task.scheduling.pool.size=5 # 线程池大小,可以用 ${} 配置
spring.task.scheduling.thread-name-prefix=task-scheduler- # 线程名前缀

也可以在配置类中自定义 TaskScheduler Bean

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
public class SchedulerConfig {

	@Bean
	public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
		ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
		scheduler.setPoolSize(5);
		scheduler.setThreadNamePrefix("task-scheduler-");
		return scheduler;
	}
}

启动SpringBoot应用,定时任务就会按照指定的频率执行。

请注意,@Scheduled注解的方法不应该有任何参数,并且返回类型应该是void。此外,@Scheduled注解的方法可以定义在配置类中,但最好将其定义在一个独立的Bean中,以便于测试和管理。

二、动态定时:数据库存放 cron 表达式,使用接口进行动态修改

本部分使用 SpringBoot 集成 MyBatis 示例来解释动态定时任务的使用,通过使用接口调用进行数据库存储,将定时任务的时间设定换成数据库获取。

虽然 @Scheduled 注解本身不支持直接从数据库读取 cron 表达式,但可以通过一些设计来间接实现。

关键步骤在下面第三步 TaskSchedule 中。

以下是个人写的一个从数据库获取 cron 表达式来进行实时更新的 demo,有的地方并不完善。

文中的 MyBatis 集成如果有不明白可以看 SpringBoot 项目整合 MyBatisPlus 框架,附带测试示例

1、TaskController

在这个地方,先调用 http://localhost:8080/api/update 进行修改 cron 表达式。

然后使用 http://localhost:8080/api/update-cron 接口,实现提醒定时任务更换 cron 表达式,重新运行。

这样就实现了,不重启任务就可以实现定时任务的动态变化。

package com.wen.controller;

import com.wen.TaskSchedule;
import com.wen.dto.CronList;
import com.wen.service.TaskService;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
public class TaskController {

    @Autowired
    private TaskService taskService;

    @GetMapping("select")
    public CronList selectById(@Param("id") int id) {
        return taskService.selectById(id);
    }

    @PostMapping("add")
    public void addCron(@RequestBody CronList cronList) {
        taskService.addCron(cronList);
    }

    @GetMapping("update")
    public void updateCron(@Param("id") int id, @Param("cron") String cron) {
        taskService.updateCron(id, cron);
    }

    @GetMapping("delete")
    public void deleteCron(@Param("id") int id) {
        taskService.removeCron(id);
    }

    @Autowired
    private TaskSchedule taskSchedule;

    @GetMapping("update-cron")
    public String updateScheduledTask() {
        taskSchedule.startOrUpdateScheduledTask();
        return "success";
    }
}

2、SchedulerConfig

package com.wen.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
public class SchedulerConfig {

    @Bean
    public ThreadPoolTaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(10);
        scheduler.setThreadNamePrefix("wen-");
        scheduler.setDaemon(true);
        return scheduler;
    }

}

3、TaskSchedule

在Java中,直接使用 @Scheduled 注解并不支持直接通过监听器动态修改 cron 表达式,因为 @Scheduled 注解是在 Spring 容器启动时解析的,并且 cron 表达式是在那时被解析和固定下来的。

可以通过一些方法间接实现这个需求,比如使用 TaskScheduler 接口动态地添加、更新或取消定时任务。但是这种方式也会有一定限制。

package com.wen;

import com.wen.service.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;

import java.util.concurrent.ScheduledFuture;

@Service
public class TaskSchedule {

	// 这里需要定义 SchedulerConfig 
    @Autowired
    private TaskScheduler taskScheduler;

    private ScheduledFuture<?> scheduledFuture;

    @Autowired
    private TaskService taskService;

    public String getCronByiD() {
        return taskService.selectById(1).getCron();
    }

    public void startOrUpdateScheduledTask() {
        Runnable task = () -> System.out.println("Task is running at " + System.currentTimeMillis());
        String cronExpression = getCronByiD();

        // 如果有任务运行,先取消
        if( scheduledFuture != null) {
            scheduledFuture.cancel(false);
        }

        // 使用新的cron表达式
        scheduledFuture = taskScheduler.schedule(task, new CronTrigger(cronExpression));
    }
}

4、CronList

package com.wen.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CronList {

    private int id;

    private String cron;
}

5、CronListMapper

package com.wen.mapper;

import com.wen.dto.CronList;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface CronListMapper {

    CronList selectById(int id);

    void addCron(CronList cronList);

    void updateCron(int id, String cron);

    void removeCron(int id);

}

6、TaskServiceImpl

package com.wen.service.impl;

import com.wen.dto.CronList;
import com.wen.mapper.CronListMapper;
import com.wen.service.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class TaskServiceImpl implements TaskService {

    @Autowired
    private CronListMapper cronListMapper;

    @Override
    public CronList selectById(int id) {
        return cronListMapper.selectById(id);
    }

    @Override
    public void addCron(CronList cronList) {
        cronListMapper.addCron(cronList);
    }

    @Override
    public void updateCron(int id, String cron) {
        cronListMapper.updateCron(id, cron);
    }

    @Override
    public void removeCron(int id) {
        cronListMapper.removeCron(id);
    }
}

7、TaskService

package com.wen.service;

import com.wen.dto.CronList;

public interface TaskService {

    CronList selectById(int id);

    void addCron(CronList cronList);

    void updateCron(int id, String cron);

    void removeCron(int id);
}

8、CronListMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wen.mapper.CronListMapper">

    <select id="selectById" resultType="com.wen.dto.CronList">
        SELECT id, cron
        FROM cron_list
        WHERE id = #{id}
    </select>

    <insert id="addCron" parameterType="com.wen.dto.CronList">
        INSERT INTO cron_list
        VALUES (#{id}, #{cron})
    </insert>

    <update id="updateCron" parameterType="com.wen.dto.CronList">
        UPDATE cron_list
        SET cron = #{cron}
        WHERE id = #{id}
    </update>

    <delete id="removeCron" parameterType="com.wen.dto.CronList">
        DELETE FROM cron_list
        WHERE id = #{id}
    </delete>

</mapper>

9、TaskTest

package wen;

import com.wen.TestApplication;
import com.wen.dto.CronList;
import com.wen.service.TaskService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class TaskTest {

    @Autowired
    private TaskService taskService;

    @Test
    public void addCron() {
        CronList cronList = new CronList();
        cronList.setId(1);
        cronList.setCron("0 0 2 * * ?");
        taskService.addCron(cronList);
    }

    @Test
    public void selectById(){
        CronList cronList = taskService.selectById(1);
        System.out.println(cronList);
    }
}

这里的测试只是表明数据库修改,具体上方通过接口进行调用。

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值