springboot动态任务统一管理(新增、修改、停止、删除)

springboot任务动态注册、修改、停止、删除和统一管理
github地址:https://github.com/softwarevax/maven/tree/main/unified-task-schedule
1、任务实体类

public class Job {

    /**
     * 任务id, 用于标识,默认使用全限定类名
     */
    private String jobId;

    /**
     * 任务名称, 默认简单类名
     */
    private String jobName;

    /**
     * cron表达式, 修改后即可生效
     */
    private String cron;

    /**
     * 任务描述
     */
    private String description;

    /**
     * 是否启用, 默认启用, 修改后即可生效
     */
    private boolean enable = true;

    /**
     * 是否处于等待执行下个任务的状态
     */
    private boolean active;
    /**
     * 任务运行类
     */
    private Class<? extends Runnable> clazz;
}

2、任务操作

public class JobHandler {

    private ScheduledTask scheduledTask;

    private TriggerTask triggerTask;

    private TriggerContext triggerContext;
}

3、spring动态注册bean,任务运行类中需要注入bean时,可直接注入

public class ApplicationContextUtils implements ApplicationContextAware {

    private static ApplicationContext CONTEXT;
    /**
     * 设置spring上下文
     * @param ctx spring上下文
     * @throws BeansException
     * */
    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        CONTEXT = ctx;
    }

    /**
     * 获取容器
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        return CONTEXT;
    }

    /**
     * 获取容器对象
     * @param type
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> type) {
        return CONTEXT.getBean(type);
    }

    public static <T> T getBean(String name,Class<T> clazz){
        return CONTEXT.getBean(name, clazz);
    }

    public static Object getBean(String name){
        return CONTEXT.getBean(name);
    }

    /**
     * springboot动态注册bean
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T register(Class<T> clazz) {
		ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) ApplicationContextUtils.getApplicationContext();
		DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        if(defaultListableBeanFactory.getBeanNamesForType(clazz).length > 0) {
            return defaultListableBeanFactory.getBean(clazz);
        }
		defaultListableBeanFactory.registerBeanDefinition(clazz.getName(), beanDefinitionBuilder.getRawBeanDefinition());
		return (T) ApplicationContextUtils.getBean(clazz.getName());
    }
}

4、配置SchedulingConfigurer,实现动态任务的关键

public class JobSchedulingConfigurer implements SchedulingConfigurer {

    private ScheduledTaskRegistrar registrar;

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        this.registrar = scheduledTaskRegistrar;
    }

    public ScheduledTaskRegistrar getRegistrar() {
        return registrar;
    }

    public void setRegistrar(ScheduledTaskRegistrar registrar) {
        this.registrar = registrar;
    }
}

5、任务增删改查

public class SchedulerManager {

    /**
     * 任务容器
     */
    private Map<Job, JobHandler> tasks = new ConcurrentHashMap<>();

    /**
     * 任务注册
     */
    @Autowired
    private JobSchedulingConfigurer register;

    /**
     * 新增任务, 自生效
     * @param job 任务实体
     * @return 返回新增的任务
     */
    public Job addJob(Job job) {
        Assert.notNull(job, "job can't be null");
        ScheduledTaskRegistrar registrar = register.getRegistrar();
        Runnable runnable = ApplicationContextUtils.register(job.getClazz());
        if(job.getJobId() == null || "".equals(job.getJobId())) {
            job.setJobId(job.getClazz().getName());
        }
        Assert.isNull(this.getJob(job.getJobId()), "任务[" + job.getJobId() + "]已存在");
        if(job.getJobName() == null || "".equals(job.getJobName())) {
            job.setJobName(ClassUtils.getShortName(job.getClazz()));
        }
        CronExpress cron = AnnotationUtils.findAnnotation(job.getClazz(), CronExpress.class);
        if(cron != null && !"".equals(cron.value())) {
            // 注解的属性,大于配置的属性,方便调试
            job.setCron(cron.value());
        }
        job.setEnable(true);
        job.setActive(true);
        JobHandler entity = new JobHandler();
        TriggerTask triggerTask = new TriggerTask(runnable, (TriggerContext triggerContext) -> {
            // 每次任务执行均会进入此方法
            CronTrigger trigger = new CronTrigger(job.getCron());
            entity.setTriggerContext(triggerContext);
            return job.isEnable() ? trigger.nextExecutionTime(triggerContext) : null;
        });
        ScheduledTask scheduledTask = registrar.scheduleTriggerTask(triggerTask);
        entity.setScheduledTask(scheduledTask);
        entity.setTriggerTask(triggerTask);
        tasks.put(job, entity);
        return job;
    }

    /**
     * 任务类(必须标注了@CronExpress注解,且实现了Runnable接口)
     * @param clazz 接口类
     * @return 任务对象
     */
    public Job addJob(Class<? extends Runnable> clazz) {
        Job job = new Job();
        job.setClazz(clazz);
        return this.addJob(job);
    }

    /**
     * 获取任务操作对象
     * @param jobId 任务id
     * @return 任务操作对象
     */
    public JobHandler getJobHandler(String jobId) {
        return tasks.get(new Job(jobId));
    }

    /**
     * 根据任务id获取任务
     * @param jobId 任务id
     * @return 任务实体
     */
    public Job getJob(String jobId) {
        Assert.hasText(jobId, "jobId can't be null");
        Set<Job> jobs = tasks.keySet();
        if(jobs.size() == 0) {
            return null;
        }
        Iterator<Job> iterator = jobs.iterator();
        while (iterator.hasNext()) {
            Job next = iterator.next();
            if(jobId.equals(next.getJobId())) {
                return next;
            }
        }
        return null;
    }

    /**
     * 关闭任务(若任务正在执行,待任务执行完)
     * @param jobId 任务id
     * @return 是否关闭成功
     */
    public boolean shutDown(String jobId) {
        try {
            JobHandler handler = this.getJobHandler(jobId);
            Assert.notNull(handler, "任务[" + jobId + "]不存在");
            handler.getScheduledTask().cancel();
            Job job = getJob(jobId);
            job.setActive(false);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 启动已经注册的任务
     * @param jobId 任务id
     * @return 是否成功启动
     */
    public boolean startUp(String jobId) {
        try {
            JobHandler handler = this.getJobHandler(jobId);
            Assert.notNull(handler, "任务[" + jobId + "]不存在");
            register.getRegistrar().scheduleTriggerTask(handler.getTriggerTask());
            Job job = getJob(jobId);
            job.setActive(true);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取所有的任务实体
     * @return
     */
    public List<Job> getJobs() {
        return new ArrayList<>(tasks.keySet());
    }

    /**
     * 删除任务,先关闭再删除
     * @param jobId
     * @return
     */
    public boolean deleteJob(String jobId) {
        try {
            Job job = this.getJob(jobId);
            Assert.notNull(job, "任务[" + jobId + "]不存在");
            shutDown(jobId);
            tasks.remove(job);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

6、任务运行类

@CronExpress("0/1 * * * * *")
public class HelloJob implements Runnable {

    @Override
    public void run() {
        System.out.println("hello msg" + Thread.currentThread().getId());
    }
}

7、测试

ConfigurableApplicationContext ctx = SpringApplication.run(UnifiedTaskScheduleStarterApplication.class, args);
SchedulerManager schedulerManager = ctx.getBean(SchedulerManager.class);
Job job = schedulerManager.addJob(com.github.softwarevax.web.HelloJob.class);
schedulerManager.shutDown(job.getJobId());

所有的任务,放在一个模块中,该模块不需要特殊依赖包,启动时,注册任务,实现统一管理

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值