Spring Boot realiza tareas dinámicas de adición, eliminación, inicio y parada de tiempo

En el proyecto de arranque de primavera, puede implementar tareas cronometradas a través de la anotación @EnableScheduling y la anotación @Scheduled, o puede implementar tareas cronometradas a través de la interfaz SchedulingConfigurer. Pero estos dos métodos no pueden agregar, eliminar, iniciar y detener tareas de manera dinámica. Para realizar la función de agregar y eliminar dinámicamente tareas de cronometraje de inicio y detención, un enfoque más extenso es integrar el marco Quartz. Pero mi principio de desarrollo es: para satisfacer las necesidades del proyecto, confíe en otros marcos lo menos posible para evitar que el proyecto sea demasiado hinchado y complicado. Verifique el código fuente de la clase org.springframework.scheduling.ScheduledTaskRegistrar en el paquete jar spring-context y descubra que puede agregar, eliminar, iniciar y detener dinámicamente tareas programadas modificando esta clase.

 

Página de lista de tareas programadas

 

Registro de ejecución de tareas programadas

 

Agregar una clase de configuración de grupo de subprocesos para ejecutar tareas de tiempo

 

@Configuration
public class SchedulingConfig {
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        // 定时任务执行线程池核心线程数
        taskScheduler.setPoolSize(4);
        taskScheduler.setRemoveOnCancelPolicy(true);
        taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");
        return taskScheduler;
    }
}

Agregue una clase contenedora para ScheduledFuture. ScheduledFuture es el resultado de la ejecución del grupo de subprocesos de tareas programadas de ScheduledExecutorService.

 

public final class ScheduledTask {

    volatile ScheduledFuture<?> future;

    /**
     * 取消定时任务
     */
    public void cancel() {
        ScheduledFuture<?> future = this.future;
        if (future != null) {
            future.cancel(true);
        }
    }
}

Agregue la clase de implementación de la interfaz Runnable, que es llamada por el grupo de subprocesos de tareas temporizadas para ejecutar los métodos en el bean especificado.

 

public class SchedulingRunnable implements Runnable {

    private static final Logger logger = LoggerFactory.getLogger(SchedulingRunnable.class);

    private String beanName;

    private String methodName;

    private String params;

    public SchedulingRunnable(String beanName, String methodName) {
        this(beanName, methodName, null);
    }

    public SchedulingRunnable(String beanName, String methodName, String params) {
        this.beanName = beanName;
        this.methodName = methodName;
        this.params = params;
    }

    @Override
    public void run() {
        logger.info("定时任务开始执行 - bean:{},方法:{},参数:{}", beanName, methodName, params);
        long startTime = System.currentTimeMillis();

        try {
            Object target = SpringContextUtils.getBean(beanName);

            Method method = null;
            if (StringUtils.isNotEmpty(params)) {
                method = target.getClass().getDeclaredMethod(methodName, String.class);
            } else {
                method = target.getClass().getDeclaredMethod(methodName);
            }

            ReflectionUtils.makeAccessible(method);
            if (StringUtils.isNotEmpty(params)) {
                method.invoke(target, params);
            } else {
                method.invoke(target);
            }
        } catch (Exception ex) {
            logger.error(String.format("定时任务执行异常 - bean:%s,方法:%s,参数:%s ", beanName, methodName, params), ex);
        }

        long times = System.currentTimeMillis() - startTime;
        logger.info("定时任务执行结束 - bean:{},方法:{},参数:{},耗时:{} 毫秒", beanName, methodName, params, times);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SchedulingRunnable that = (SchedulingRunnable) o;
        if (params == null) {
            return beanName.equals(that.beanName) &&
                    methodName.equals(that.methodName) &&
                    that.params == null;
        }

        return beanName.equals(that.beanName) &&
                methodName.equals(that.methodName) &&
                params.equals(that.params);
    }

    @Override
    public int hashCode() {
        if (params == null) {
            return Objects.hash(beanName, methodName);
        }

        return Objects.hash(beanName, methodName, params);
    }
}

Agregue la clase de registro de tareas programadas para agregar y eliminar tareas programadas.

 

@Component
public class CronTaskRegistrar implements DisposableBean {

    private final Map<Runnable, ScheduledTask> scheduledTasks = new ConcurrentHashMap<>(16);

    @Autowired
    private TaskScheduler taskScheduler;

    public TaskScheduler getScheduler() {
        return this.taskScheduler;
    }

    public void addCronTask(Runnable task, String cronExpression) {
        addCronTask(new CronTask(task, cronExpression));
    }

    public void addCronTask(CronTask cronTask) {
        if (cronTask != null) {
            Runnable task = cronTask.getRunnable();
            if (this.scheduledTasks.containsKey(task)) {
                removeCronTask(task);
            }

            this.scheduledTasks.put(task, scheduleCronTask(cronTask));
        }
    }

    public void removeCronTask(Runnable task) {
        ScheduledTask scheduledTask = this.scheduledTasks.remove(task);
        if (scheduledTask != null)
            scheduledTask.cancel();
    }

    public ScheduledTask scheduleCronTask(CronTask cronTask) {
        ScheduledTask scheduledTask = new ScheduledTask();
        scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());

        return scheduledTask;
    }


    @Override
    public void destroy() {
        for (ScheduledTask task : this.scheduledTasks.values()) {
            task.cancel();
        }

        this.scheduledTasks.clear();
    }
}

Agregar clase de ejemplo de tarea cronometrada

 

@Component("demoTask")
public class DemoTask {
    public void taskWithParams(String params) {
        System.out.println("执行有参示例任务:" + params);
    }

    public void taskNoParams() {
        System.out.println("执行无参示例任务");
    }
}

Diseño de tabla de base de datos de tareas de temporización

 

Diseño de tabla de base de datos de tareas de temporización

 

Agregar clase de entidad de tarea cronometrada

 

public class SysJobPO {
    /**
     * 任务ID
     */
    private Integer jobId;
    /**
     * bean名称
     */
    private String beanName;
    /**
     * 方法名称
     */
    private String methodName;
    /**
     * 方法参数
     */
    private String methodParams;
    /**
     * cron表达式
     */
    private String cronExpression;
    /**
     * 状态(1正常 0暂停)
     */
    private Integer jobStatus;
    /**
     * 备注
     */
    private String remark;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 更新时间
     */
    private Date updateTime;

    public Integer getJobId() {
        return jobId;
    }

    public void setJobId(Integer jobId) {
        this.jobId = jobId;
    }

    public String getBeanName() {
        return beanName;
    }

    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public String getMethodParams() {
        return methodParams;
    }

    public void setMethodParams(String methodParams) {
        this.methodParams = methodParams;
    }

    public String getCronExpression() {
        return cronExpression;
    }

    public void setCronExpression(String cronExpression) {
        this.cronExpression = cronExpression;
    }

    public Integer getJobStatus() {
        return jobStatus;
    }

    public void setJobStatus(Integer jobStatus) {
        this.jobStatus = jobStatus;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

}

Nueva tarea cronometrada

 

Nueva tarea cronometrada

 

boolean success = sysJobRepository.addSysJob(sysJob);
if (!success)
    return OperationResUtils.fail("新增失败");
else {
    if (sysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())) {
        SchedulingRunnable task = new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(), sysJob.getMethodParams());
        cronTaskRegistrar.addCronTask(task, sysJob.getCronExpression());
    }
}

return OperationResUtils.success();

Modifique la tarea programada, primero elimine la tarea original y luego inicie la nueva tarea

 

boolean success = sysJobRepository.editSysJob(sysJob);
if (!success)
    return OperationResUtils.fail("编辑失败");
else {
    //先移除再添加
    if (existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())) {
        SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());
        cronTaskRegistrar.removeCronTask(task);
    }

    if (sysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())) {
        SchedulingRunnable task = new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(), sysJob.getMethodParams());
        cronTaskRegistrar.addCronTask(task, sysJob.getCronExpression());
    }
}

return OperationResUtils.success();

Eliminar tareas programadas

 

boolean success = sysJobRepository.deleteSysJobById(req.getJobId());
if (!success)
    return OperationResUtils.fail("删除失败");
else{
    if (existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())) {
        SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());
        cronTaskRegistrar.removeCronTask(task);
    }
}

return OperationResUtils.success();

Interruptor de estado de inicio / parada de la tarea del temporizador

 

if (existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())) {
    SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());
    cronTaskRegistrar.addCronTask(task, existedSysJob.getCronExpression());
} else {
    SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());
    cronTaskRegistrar.removeCronTask(task);
}

Agregue la clase SysJobRunner que implementa la interfaz CommandLineRunner.Cuando se inicia el proyecto de arranque de primavera, cargue las tareas regulares en la base de datos que están en el estado normal.

 

@Service
public class SysJobRunner implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(SysJobRunner.class);

    @Autowired
    private ISysJobRepository sysJobRepository;

    @Autowired
    private CronTaskRegistrar cronTaskRegistrar;

    @Override
    public void run(String... args) {
        // 初始加载数据库里状态为正常的定时任务
        List<SysJobPO> jobList = sysJobRepository.getSysJobListByStatus(SysJobStatus.NORMAL.ordinal());
        if (CollectionUtils.isNotEmpty(jobList)) {
            for (SysJobPO job : jobList) {
                SchedulingRunnable task = new SchedulingRunnable(job.getBeanName(), job.getMethodName(), job.getMethodParams());
                cronTaskRegistrar.addCronTask(task, job.getCronExpression());
            }

            logger.info("定时任务已加载完毕...");
        }
    }
}

Clase de herramienta SpringContextUtils, utilizada para obtener frijoles del contenedor de primavera

 

@Component
public class SpringContextUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        SpringContextUtils.applicationContext = applicationContext;
    }

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

    public static <T> T getBean(Class<T> requiredType) {
        return applicationContext.getBean(requiredType);
    }

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

    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }

    public static Class<? extends Object> getType(String name) {
        return applicationContext.getType(name);
    }
}

 

Supongo que te gusta

Origin blog.csdn.net/baidu_39322753/article/details/104805510
Recomendado
Clasificación