java
主页 > 软件编程 > java >

SpringBoot实现动态添加定时任务功能

2022-02-28 | 秩名 | 点击:

最近的需求有一个自动发布的功能, 需要做到每次提交都要动态的添加一个定时任务

代码结构

1. 配置类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

package com.orion.ops.config;

  

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.scheduling.TaskScheduler;

import org.springframework.scheduling.annotation.EnableScheduling;

import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

/**

 * 调度器配置

 *

 * @author Jiahang Li

 * @version 1.0.0

 * @since 2022/2/14 9:51

 */

@EnableScheduling

@Configuration

public class SchedulerConfig {

    @Bean

    public TaskScheduler taskScheduler() {

        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();

        scheduler.setPoolSize(4);

        scheduler.setRemoveOnCancelPolicy(true);

        scheduler.setThreadNamePrefix("scheduling-task-");

        return scheduler;

    }

}

2. 定时任务类型枚举

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

package com.orion.ops.handler.scheduler;

  

import com.orion.ops.consts.Const;

import com.orion.ops.handler.scheduler.impl.ReleaseTaskImpl;

import com.orion.ops.handler.scheduler.impl.SchedulerTaskImpl;

import lombok.AllArgsConstructor;

import java.util.function.Function;

/**

 * 任务类型

 *

 * @author Jiahang Li

 * @version 1.0.0

 * @since 2022/2/14 10:16

 */

@AllArgsConstructor

public enum TaskType {

    /**

     * 发布任务

     */

    RELEASE(id -> new ReleaseTaskImpl((Long) id)) {

        @Override

        public String getKey(Object params) {

            return Const.RELEASE + "-" + params;

        }

    },

     * 调度任务

    SCHEDULER_TASK(id -> new SchedulerTaskImpl((Long) id)) {

            return Const.TASK + "-" + params;

    ;

    private final Function<Object, Runnable> factory;

     * 创建任务

     *

     * @param params params

     * @return task

    public Runnable create(Object params) {

        return factory.apply(params);

    }

     * 获取 key

     * @return key

    public abstract String getKey(Object params);

}

这个枚举的作用是生成定时任务的 runnable 和 定时任务的唯一值, 方便后续维护

3. 实际执行任务实现类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

package com.orion.ops.handler.scheduler.impl;

  

import com.orion.ops.service.api.ApplicationReleaseService;

import com.orion.spring.SpringHolder;

import lombok.extern.slf4j.Slf4j;

/**

 * 发布任务实现

 *

 * @author Jiahang Li

 * @version 1.0.0

 * @since 2022/2/14 10:25

 */

@Slf4j

public class ReleaseTaskImpl implements Runnable {

    protected static ApplicationReleaseService applicationReleaseService = SpringHolder.getBean(ApplicationReleaseService.class);

    private Long releaseId;

    public ReleaseTaskImpl(Long releaseId) {

        this.releaseId = releaseId;

    }

    @Override

    public void run() {

        log.info("定时执行发布任务-触发 releaseId: {}", releaseId);

        applicationReleaseService.runnableAppRelease(releaseId, true);

}

4. 定时任务包装器

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

package com.orion.ops.handler.scheduler;

  

import org.springframework.scheduling.TaskScheduler;

import org.springframework.scheduling.Trigger;

import java.util.Date;

import java.util.concurrent.ScheduledFuture;

/**

 * 定时 任务包装器

 *

 * @author Jiahang Li

 * @version 1.0.0

 * @since 2022/2/14 10:34

 */

public class TimedTask {

    /**

     * 任务

     */

    private Runnable runnable;

     * 异步执行

    private volatile ScheduledFuture<?> future;

    public TimedTask(Runnable runnable) {

        this.runnable = runnable;

    }

     * 提交任务 一次性

     *

     * @param scheduler scheduler

     * @param time      time

    public void submit(TaskScheduler scheduler, Date time) {

        this.future = scheduler.schedule(runnable, time);

     * 提交任务 cron表达式

     * @param trigger   trigger

    public void submit(TaskScheduler scheduler, Trigger trigger) {

        this.future = scheduler.schedule(runnable, trigger);

     * 取消定时任务

    public void cancel() {

        if (future != null) {

            future.cancel(true);

        }

}

这个类的作用是包装实际执行任务, 以及提供调度器的执行方法

5. 任务注册器 (核心)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

package com.orion.ops.handler.scheduler;

  

import com.orion.ops.consts.MessageConst;

import com.orion.utils.Exceptions;

import com.orion.utils.collect.Maps;

import org.springframework.beans.factory.DisposableBean;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.scheduling.TaskScheduler;

import org.springframework.scheduling.support.CronTrigger;

import org.springframework.stereotype.Component;

import javax.annotation.Resource;

import java.util.Date;

import java.util.Map;

/**

 * 任务注册器

 *

 * @author Jiahang Li

 * @version 1.0.0

 * @since 2022/2/14 10:46

 */

@Component

public class TaskRegister implements DisposableBean {

    private final Map<String, TimedTask> taskMap = Maps.newCurrentHashMap();

    @Resource

    @Qualifier("taskScheduler")

    private TaskScheduler scheduler;

    /**

     * 提交任务

     *

     * @param type   type

     * @param time   time

     * @param params params

     */

    public void submit(TaskType type, Date time, Object params) {

        // 获取任务

        TimedTask timedTask = this.getTask(type, params);

        // 执行任务

        timedTask.submit(scheduler, time);

    }

     * @param cron   cron

    public void submit(TaskType type, String cron, Object params) {

        timedTask.submit(scheduler, new CronTrigger(cron));

     * 获取任务

    private TimedTask getTask(TaskType type, Object params) {

        // 生成任务

        Runnable runnable = type.create(params);

        String key = type.getKey(params);

        // 判断是否存在任务

        if (taskMap.containsKey(key)) {

            throw Exceptions.init(MessageConst.TASK_PRESENT);

        }

        TimedTask timedTask = new TimedTask(runnable);

        taskMap.put(key, timedTask);

        return timedTask;

     * 取消任务

    public void cancel(TaskType type, Object params) {

        TimedTask task = taskMap.get(key);

        if (task != null) {

            taskMap.remove(key);

            task.cancel();

     * 是否存在

    public boolean has(TaskType type, Object params) {

        return taskMap.containsKey(type.getKey(params));

    @Override

    public void destroy() {

        taskMap.values().forEach(TimedTask::cancel);

        taskMap.clear();

}

这个类提供了执行, 提交任务的api, 实现 DisposableBean 接口, 便于在bean销毁时将任务一起销毁

6. 使用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

@Resource

private TaskRegister taskRegister;

 

/**

 * 提交发布

 */

@RequestMapping("/submit")

@EventLog(EventType.SUBMIT_RELEASE)

public Long submitAppRelease(@RequestBody ApplicationReleaseRequest request) {

    Valid.notBlank(request.getTitle());

    Valid.notNull(request.getAppId());

    Valid.notNull(request.getProfileId());

    Valid.notNull(request.getBuildId());

    Valid.notEmpty(request.getMachineIdList());

    TimedReleaseType timedReleaseType = Valid.notNull(TimedReleaseType.of(request.getTimedRelease()));

    if (TimedReleaseType.TIMED.equals(timedReleaseType)) {

        Date timedReleaseTime = Valid.notNull(request.getTimedReleaseTime());

        Valid.isTrue(timedReleaseTime.compareTo(new Date()) > 0, MessageConst.TIMED_GREATER_THAN_NOW);

    }

    // 提交

    Long id = applicationReleaseService.submitAppRelease(request);

    // 提交任务

        taskRegister.submit(TaskType.RELEASE, request.getTimedReleaseTime(), id);

    return id;

}

最后

       这是一个简单的动态添加定时任务的工具, 有很多的改造空间, 比如想持久化可以插入到库中, 定义一个 CommandLineRunner 在启动时将定时任务全部加载, 还可以给任务加钩子自动提交,自动删除等, 代码直接cv一定会报错, 就是一些工具, 常量类会报错, 改改就好了, 本人已亲测可用, 有什么问题可以在评论区沟通

原文链接:https://blog.csdn.net/qq_41011894/article/details/123116277
相关文章
最新更新