跳到主要内容

动态定时任务

使用的是springboot自带的定时任务:TaskScheduler,可动态控制定时任务。

定时任务已实现持久化,持久化方式为数据库,所以第一步需要建个存储任务的表,要求要有字段:enable_flag(是否开启任务)、cron_el(cron表达式)、identifier(任务标识符),SQL语句:

  `enable_flag` tinyint unsigned DEFAULT '0',
`cron_el` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
`identifier` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL

记得代码中引入mysql依赖和配置

  • 建持久化的表

可参考建表语句:

表名不建议使用名称wjjhook_task,因为框架内实体是使用WjjhookTask命名的。

建议命名方式为${projectName}_cron_task

建议给identifier字段增加唯一索引
CREATE TABLE `wjjhook_cron_task` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
`enable_flag` tinyint unsigned DEFAULT '0' COMMENT '是否开启任务',
`cron_el` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'cron表达式',
`identifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '任务标识符',
`memo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '描述',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
`del_flag` tinyint unsigned DEFAULT '0' COMMENT '逻辑删除',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_unique_identifier` (`identifier`,`del_flag`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

执行原理:

启动前手动在@PostConstruct方法中手动向任务池中注册任务(以便扫描到的标识符能匹配到相应任务代码),进行执行。

项目启动后会自动调用*IWjjhookTaskService.queryEnableList()*的数据,进行启动执行。

使用:

  1. 建表,参考上述建表语句。(再次提醒,表名请勿使用wjjhook_task!以下以表名wjjhook_cron_task为例)

  2. 创建三个类(实现方法的拓展):

    • sql层:WjjhookCronTaskMapper.xml

      namespace需要根据自己包名设置

      <?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.example.demo.mapper.WangjjTaskMapper">

      <select id="list" resultType="cn.skyisazure.wjjhook.module.WjjhookTaskModule">
      SELECT
      enable_flag,
      cron_el,
      identifier
      FROM `wjjhook_cron_task`
      WHERE del_flag = 0
      </select>
      <update id="updateTaskByIdentifier">
      UPDATE `wjjhook_cron_task`
      SET cron_el = #{cronEl}
      WHERE identifier = #{identifier}
      AND del_flag = 0
      </update>
      <update id="enableTaskByIdentifier">
      UPDATE `wjjhook_cron_task`
      SET enable_flag = 1
      WHERE identifier = #{identifier}
      AND del_flag = 0
      </update>
      <update id="disableTaskByIdentifier">
      UPDATE `wjjhook_cron_task`
      SET enable_flag = 0
      WHERE identifier = #{identifier}
      AND del_flag = 0
      </update>
      <update id="removeTask">
      UPDATE `wjjhook_cron_task`
      SET del_flag = 1
      WHERE identifier = #{identifier}
      AND del_flag = 0
      </update>
      </mapper>
    • Dao层:WjjhookCronTaskMapper

      该类要被@MapperScan扫描到。

      /**
      * 定时任务
      * @author wjian
      */
      public interface WangjjTaskMapper {

      /**
      * 返回所有定时任务
      * @return
      */
      List<WjjhookTaskModule> list();
      /**
      * 修改定时任务表达式
      * @param identifier
      * @param cronEl
      */
      void updateTaskByIdentifier(@Param("identifier") String identifier,@Param("cronEl") String cronEl);
      /**
      * 开启定时任务
      * @param identifier
      */
      void enableTaskByIdentifier(@Param("identifier") String identifier);
      /**
      * 禁用定时任务
      * @param identifier
      */
      void disableTaskByIdentifier(@Param("identifier") String identifier);
      /**
      * 删除定时任务
      * @param identifier
      */
      void removeTask(@Param("identifier") String identifier);
      }
    • 实现类:WjjhookTaskServiceImpl


      /**
      * 任务持久化实现类
      * @author wangjj
      */
      @Service
      @RequiredArgsConstructor
      public class WjjhookTaskServiceImpl implements IWjjhookTaskService {

      private final WangjjTaskMapper wangjjTaskMapper;


      /**
      * 返回所有定时任务
      * @return
      */
      @Override
      public List<WjjhookTaskModule> list() {
      return wangjjTaskMapper.list();
      }

      /**
      * 修改定时任务表达式
      *
      * @param identifier
      * @param cronEl
      * @return
      */
      @Override
      public void updateTaskByIdentifier(String identifier, String cronEl) {
      wangjjTaskMapper.updateTaskByIdentifier(identifier, cronEl);
      }

      /**
      * 开启定时任务
      * @param identifier
      * @return
      */
      @Override
      public void enableTaskByIdentifier(String identifier) {
      wangjjTaskMapper.enableTaskByIdentifier(identifier);
      }

      /**
      * 禁用定时任务
      *
      * @param identifier
      * @return
      */
      @Override
      public void disableTaskByIdentifier(String identifier) {
      wangjjTaskMapper.disableTaskByIdentifier(identifier);
      }

      /**
      * 删除定时任务
      *
      * @param identifier
      * @return
      */
      @Override
      public void removeTask(String identifier) {
      wangjjTaskMapper.removeTask(identifier);
      }
      }
  3. 定义任务:

    建一个存放定时任务的包:com.example.core.task,在该包下新建自己的任务类:

    /**
    * 打印当前当前时间
    * @author wangjj
    */
    public class SimpleTask implements Runnable{
    @Override
    public void run() {
    System.out.println("当前时间:"+LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    }
    }
  4. 注册任务(将第3步中定义的任务注册到任务中):

    推荐第二种:自动注册。

  • 第一种,手动注册: 在启动类中加入@PostConstruct注解的方法,注册任务示例:

    @SpringBootApplication
    @MapperScan({"com.example.*.mapper"})
    @EnableWjjhook(apiSelectorPackages = {
    @ApiSelectorPackage(value = "com.example.demo.controller"),
    })
    public class ExampleDemoApplication {

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

    /**
    * 注册任务
    */
    @PostConstruct
    public void registrarRunnable(){
    TaskRegistrar taskRegistrar = SpringUtil.getBean(TaskRegistrar.class);
    taskRegistrar.register("printTaskPool",new SimpleTask());
    }
    }
  • 第二种,自动注册:

    借助于@Task注解,创建任务类,实现接口Runnable

    优势:可使用@Autowired、@Resource注入bean,无需通过多余代码进行注入。

    @Task(identifier = "simpleTask")
    public class SimpleTask implements Runnable {

    @Resource
    private UserServer userServer;

    @Override
    public void run() {
    System.out.println(getClass().getSimpleName()+",当前时间:"+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
    }

    }
  1. 最后在数据库中加入任务:

    示例:

    INSERT INTO `wjjhook_cron_task` 
    (`id`, `enable_flag`, `cron_el`, `identifier`, `memo`, `create_time`, `create_by`, `update_time`, `update_by`, `del_flag`)
    VALUES
    (5, 0, '*/1 * * * * ?', 'demo', '测试用', '2022-12-31 23:59:59', NULL, NULL, NULL, 0);

最后

注册任务那,手动注册,我建议按上述方式去写注册,其实也可以在SpringApplication.run(NiceDemoApplication.class, args);后注册。