1. scheduled 方案

  1. 创建 spring boot 工程
  2. 在需要定时执行的类上添加注解 @Component
  3. 在类中定义方法, 编写定时业务逻辑, 并在方法上定义注解 @Scheduled , 并配置定时执行的周期
  4. 在 spring boot 引导类添加注解 @EnableScheduling

    1. @Component
    2. public class SchedulerTest{
    3. @Scheduled(fixedRate=1000)
    4. public void showTime(){
    5. System.out.println(new Date().toLocaleString());
    6. }
    7. }

    @Scheduled 介绍

  5. fixedRate 按照指定周期执行, 单位毫秒

    1. @Scheduled(fixedRate=1000)
  6. fixedRateString 和 fixedRate 类似, 只是是一个字符串, 时间也是毫秒值
  7. fixedDelay 固定延迟执行. 方法执行结束之后, 在固定延迟执行.
  8. fiexdDelayString 和 fixedDelay 类似
  9. Cron 表达式

    Cron 表达式是字符串, 实际是有七段值分别去描述时间,这些值是通过空白 区分

    1. Seconds 秒
    2. Minutes 分
    3. Hours 小时
    4. Day-of-Month 每月的第几天
    5. Month 第几月
    6. Day-of-Week 一周第几天
    7. Year (可选字段) 年

2. SchedulingConfigurer

业务场景描述:定时任务的任务名称,cron(定时任务表达式),定时任务开关,存储在数据库表中。在不重启项目的情况下,修改定时任务表达式,可以实时获取新的定时任务执行时间规则;修改定时任务执行状态,可以随时开关定时任务。
使用技术:基于接口 SchedulingConfigurer

  1. DROP TABLE IF EXISTS `scheduled`;
  2. CREATE TABLE `scheduled` (
  3. `name` varchar(20) DEFAULT NULL,
  4. `cron` varchar(30) DEFAULT NULL,
  5. `open` tinyint(1) DEFAULT NULL COMMENT '1开启, 其他为关闭'
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  7. -- ----------------------------
  8. -- Records of scheduled
  9. -- ----------------------------
  10. INSERT INTO `scheduled` VALUES ('demo1', '0/5 * * * * ?', '1');
  11. INSERT INTO `scheduled` VALUES ('demo2', '0/6 * * * * ?', '1');

代码实现

定时任务配置类

  1. package com.example.demo.scheduled;
  2. import com.google.common.util.concurrent.ThreadFactoryBuilder;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.scheduling.annotation.EnableScheduling;
  6. import org.springframework.scheduling.annotation.SchedulingConfigurer;
  7. import org.springframework.scheduling.config.ScheduledTaskRegistrar;
  8. import org.springframework.scheduling.support.CronTrigger;
  9. import java.util.concurrent.Executor;
  10. import java.util.concurrent.Executors;
  11. import java.util.concurrent.ThreadFactory;
  12. /**
  13. * @Description: 基于接口SchedulingConfigurer的动态定时任务
  14. * @Author:
  15. */
  16. @Configuration
  17. @EnableScheduling
  18. public abstract class ConfigurerSchedulingDemo implements SchedulingConfigurer {
  19. //定时任务周期表达式
  20. private String cron;
  21. /**
  22. * @Description: 重写配置定时任务的方法
  23. * @param: scheduledTaskRegistrar
  24. * @return: void
  25. * @Author:
  26. */
  27. @Override
  28. public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
  29. scheduledTaskRegistrar.setScheduler(taskScheduler());
  30. scheduledTaskRegistrar.addTriggerTask(
  31. //执行定时任务
  32. () ->
  33. {
  34. taskService();
  35. },
  36. //设置触发器
  37. triggerContext -> {
  38. cron = getCron();//获取定时任务周期表达式
  39. CronTrigger trigger = new CronTrigger(cron);
  40. return trigger.nextExecutionTime(triggerContext);
  41. }
  42. );
  43. }
  44. @Bean
  45. public Executor taskScheduler() {
  46. //设置线程名称
  47. ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build();
  48. //创建线程池
  49. return Executors.newScheduledThreadPool(5, namedThreadFactory);
  50. }
  51. /**
  52. * @Description: 执行定时任务
  53. * @param:
  54. * @return: void
  55. * @Author:
  56. */
  57. public abstract void taskService();
  58. /**
  59. * @Description: 获取定时任务周期表达式
  60. * @param:
  61. * @return: java.lang.String
  62. * @Author:
  63. * @Date: 2020/8/28
  64. */
  65. public abstract String getCron();
  66. }

定时任务1

  1. package com.example.demo.scheduled;
  2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  3. import com.example.demo.dao.CronMapper;
  4. import com.example.demo.entity.Scheduled;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.context.annotation.Configuration;
  7. import java.time.LocalDateTime;
  8. /**
  9. * @Description:
  10. * @Author:
  11. */
  12. @Configuration
  13. public class TaskDemo extends ConfigurerSchedulingDemo {
  14. @Autowired //注入mapper
  15. @SuppressWarnings("all")
  16. CronMapper cronMapper;
  17. @Override
  18. public void taskService() {
  19. Integer open = getOpen();
  20. if (1== open){
  21. System.out.println("定时任务demo1:"
  22. + LocalDateTime.now()+",线程名称:"+Thread.currentThread().getName()
  23. + " 线程id:"+Thread.currentThread().getId());
  24. }
  25. }
  26. @Override
  27. public String getCron() {
  28. QueryWrapper<Scheduled> queryWrapper = new QueryWrapper<>();
  29. queryWrapper.eq("name","demo1");
  30. String cron = cronMapper.selectOne(queryWrapper).getCron();
  31. return cron;
  32. }
  33. public Integer getOpen() {
  34. QueryWrapper<Scheduled> queryWrapper = new QueryWrapper<>();
  35. queryWrapper.eq("name", "demo1");
  36. Integer open = cronMapper.selectOne(queryWrapper).getOpen();
  37. return open;
  38. }
  39. }

定时任务2

  1. package com.example.demo.scheduled;
  2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  3. import com.example.demo.dao.CronMapper;
  4. import com.example.demo.entity.Scheduled;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.context.annotation.Configuration;
  7. import java.time.LocalDateTime;
  8. /**
  9. * @Description:
  10. * @Author:
  11. */
  12. @Configuration
  13. public class TaskDemo1 extends ConfigurerSchedulingDemo {
  14. @Autowired //注入mapper
  15. @SuppressWarnings("all")
  16. CronMapper cronMapper;
  17. @Override
  18. public void taskService() {
  19. Integer open = getOpen();
  20. if (1== open){
  21. System.out.println("定时任务demo2:"
  22. + LocalDateTime.now()+",线程名称:"+Thread.currentThread().getName()
  23. + " 线程id:"+Thread.currentThread().getId());
  24. }
  25. }
  26. @Override
  27. public String getCron() {
  28. QueryWrapper<Scheduled> queryWrapper = new QueryWrapper<>();
  29. queryWrapper.eq("name","demo2");
  30. String cron = cronMapper.selectOne(queryWrapper).getCron();
  31. return cron;
  32. }
  33. public Integer getOpen() {
  34. QueryWrapper<Scheduled> queryWrapper = new QueryWrapper<>();
  35. queryWrapper.eq("name", "demo2");
  36. Integer open = cronMapper.selectOne(queryWrapper).getOpen();
  37. return open;
  38. }
  39. }

2. Hutool 定时器实现

1. 定义接口

  1. public interface TimerTaskRunner {
  2. /**
  3. * 所有定时器业务类必须实现此接口
  4. */
  5. public void action();
  6. }

所有定时器类必须实现此接口

2. 定义定时器实现类

  1. /**
  2. * 本接口用来,屏蔽定时任务的多样性
  3. * <p>
  4. * 目前用hutool,不排除以后用别的
  5. */
  6. public interface TimerExeService {
  7. /**
  8. * 启动一个定时器
  9. * <p>
  10. * 定时任务表达式书写规范:0/2 * * * * *
  11. * <p>
  12. * 六位数,分别是:秒 分 小时 日 月 年
  13. *
  14. * @param taskId 任务id
  15. * @param cron cron表达式
  16. * @param className 类的全名,必须是TimerTaskRunner的子类
  17. */
  18. void startTimer(String taskId, String cron, String className);
  19. /**
  20. * 停止一个定时器
  21. *
  22. * @param taskId 定时任务Id
  23. */
  24. void stopTimer(String taskId);
  25. }

3. 定义监听器

  1. /**
  2. * 定时任务启动的 listener
  3. */
  4. @Component
  5. public class TimerTaskRunListener implements ApplicationListener<ApplicationStartedEvent>, Ordered {
  6. @Override
  7. public void onApplicationEvent(ApplicationStartedEvent event) {
  8. TimersService timersService = SpringUtil.getBean(TimersService.class);
  9. TimerExeService timerExeService = SpringUtil.getBean(TimerExeService.class);
  10. // 获取所有开启状态的任务
  11. List<Timers> list = timersService.list(new LambdaQueryWrapper<Timers>().eq(Timers::getStatus, 1));
  12. // 添加定时任务到调度器
  13. for (Timers timer : list) {
  14. timerExeService.startTimer(String.valueOf(timer.getId()), timer.getCron(), timer.getActionClass());
  15. }
  16. // 设置秒级别的启用
  17. CronUtil.setMatchSecond(true);
  18. try {
  19. // TODO: 热启动的时候定时器不会重新启动,需要手动关闭,在启动一次, 生成环境可以将 try cathc 去掉就不会有此bug
  20. // 启动定时器执行器
  21. CronUtil.start();
  22. } catch (UtilException e) {
  23. CronUtil.restart();
  24. }
  25. }
  26. @Override
  27. public int getOrder() {
  28. return LOWEST_PRECEDENCE;
  29. }
  30. }

4. Hutool 定时器实现类

  1. @Slf4j
  2. @Service
  3. public class HutoolTimerExeServiceImpl implements TimerExeService {
  4. @Override
  5. public void startTimer(String taskId, String cron, String className) {
  6. log.error("定时任务开始执行...{}", className);
  7. if (ObjectUtil.hasEmpty(taskId, cron, className)) {
  8. log.error("定时任务ID,cron,className为空 ");
  9. throw new BusinessException(3, "请检查定时器的id,定时器cron表达式,定时任务是否为空!");
  10. }
  11. // 预加载类看是否存在此定时任务类
  12. try {
  13. Class.forName(className);
  14. } catch (ClassNotFoundException e) {
  15. log.error("定时任务指定类不存在。");
  16. throw new BusinessException(2, "定时任务执行类不存在");
  17. }
  18. // 定义hutool的任务
  19. Task task = () -> {
  20. try {
  21. TimerTaskRunner timerTaskRunner = (TimerTaskRunner) SpringUtil.getBean(Class.forName(className));
  22. timerTaskRunner.action();
  23. } catch (ClassNotFoundException e) {
  24. log.error(">>> 任务执行异常:{}", e.getMessage());
  25. }
  26. };
  27. // 移除定时任务
  28. CronUtil.remove(taskId);
  29. // 开始执行任务
  30. CronUtil.schedule(taskId, cron, task);
  31. }
  32. @Override
  33. public void stopTimer(String taskId) {
  34. CronUtil.remove(taskId);
  35. }
  36. }

5. 定义测试类

  1. /**
  2. * 这是一个定时任务的示例程序
  3. *
  4. */
  5. @Component
  6. public class SystemOutTaskRunner implements TimerTaskRunner {
  7. @Override
  8. public void action() {
  9. System.out.println("改革吹风吹....." + DateUtil.date());
  10. }
  11. }

6. spring boot启动类添加注解

  1. @EnableScheduling