springboot自身带有一套定时任务框架,使用起来也比较简单,只需要在应用上添加@EnableScheduling注解开启定时任务的支持,然后在具体任务实现的方法上添加 @Scheduled然后配置corn表达式就完成了。

代码示例:

1、开启定时任务的支持
package com.syx;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class MainApp {
    public static void main(String[] args) {
        SpringApplication.run(MainApp.class,args);
    }
}

2、任务方法上添加@Scheduled注解

package com.syx.task;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronSequenceGenerator;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.SimpleTriggerContext;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.TimeZone;

@Component
public class SayHelloTask {

    @Scheduled(cron = "0/50 * * * *  ?")
    public void sayHello() {
        System.out.println("hello2222 ...");

        //用于计算下次任务的执行时间
        CronTrigger cronTrigger = new CronTrigger("0/50 * * * *  ?");
        Date nextFireTime = cronTrigger.nextExecutionTime(new SimpleTriggerContext());
        System.out.println("任务下次的执行时间>>>"+nextFireTime);
    }
}

其中CronTrigger类可用于解析corn表达式,并且可以计算任务的下次触发时间。

通过上述的例子我们发现,这个任务的执行时间都是写死的,一旦应用启动后任务就会按照指定的corn表达式进行运行,如果我们想关闭或者修改任务的触发时间就需要停止应用修改代码,非常不方便缺乏灵活性,所以我们需要设计一套可以使用外部指令在不关闭应用的情况下对定时任务进行控制。

实现方案:

** 使用springboot提供的ThreadPoolTaskScheduler类进行任务的提交,然后发送http控制指令就可以实现该功能。**

代码示例:

1、配置ThreadPoolTaskScheduler

package com.syx.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
public class ScheduledConfig {
    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(3);
        threadPoolTaskScheduler.setRemoveOnCancelPolicy(true);
        return threadPoolTaskScheduler;
    }
}

2、任务控制controller

package com.syx.controller;

import com.syx.entity.ScheduledFutureHolder;
import com.syx.task.HelloTask;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.concurrent.ScheduledFuture;

@RestController
public class DoScheduledController {
    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    //存储任务执行的包装类
    private HashMap<String, ScheduledFutureHolder> scheduleMap = new HashMap<>();

    /**
     *启动任务 
     * 如果不想手动触发任务可以使用 @PostConstruct注解来启动
     */
    @RequestMapping("/start")
    public void startTask()  {
        try {
            //初始化一个任务(这里可以初始话多个)  
            HelloTask helloTask = new HelloTask();
            String corn = "0/2 * * * *  ?";
            //将任务交给任务调度器执行
            ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule(helloTask, new CronTrigger(corn));

            //将任务包装成ScheduledFutureHolder
            ScheduledFutureHolder scheduledFutureHolder = new ScheduledFutureHolder();
            scheduledFutureHolder.setScheduledFuture(schedule);
            scheduledFutureHolder.setRunnableClass(helloTask.getClass());
            scheduledFutureHolder.setCorn(corn);

            scheduleMap.put(scheduledFutureHolder.getRunnableClass().getName(),scheduledFutureHolder);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 查询所有的任务
     */
    @RequestMapping("/queryTask")
    public void queryTask(){
        scheduleMap.forEach((k,v)->{
            System.out.println(k+"  "+v);
        });
    }

    /**
     * 停止任务
     * @param className
     */
    @RequestMapping("/stop/{className}")
    public void stopTask(@PathVariable  String className){
        if(scheduleMap.containsKey(className)){//如果包含这个任务
            ScheduledFuture<?> scheduledFuture = scheduleMap.get(className).getScheduledFuture();
            if(scheduledFuture!=null){
                scheduledFuture.cancel(true);
            }
        }
    }


    /**
     * 重启任务,修改任务的触发时间
     * @param className
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    @RequestMapping("/restart/{className}")
    public void restartTask(@PathVariable String className) throws InstantiationException, IllegalAccessException {
        if(scheduleMap.containsKey(className)){//如果包含这个任务


            //这里的corn可以通过前端动态传过来
            String corn = "0/50 * * * *  ?";
            ScheduledFutureHolder scheduledFutureHolder = scheduleMap.get(className);
            ScheduledFuture<?> scheduledFuture = scheduledFutureHolder.getScheduledFuture();
            if(scheduledFuture!=null){
                //先停掉任务
                scheduledFuture.cancel(true);

                //修改触发时间重新启动任务
                Runnable runnable = scheduledFutureHolder.getRunnableClass().newInstance();

                ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule(runnable, new CronTrigger(corn));

                scheduledFutureHolder.setScheduledFuture(schedule);
                scheduledFutureHolder.setCorn(corn);

                scheduleMap.put(scheduledFutureHolder.getRunnableClass().getName(),scheduledFutureHolder);
            }
        }
    }
}

3、实现需求的具体任务(必须实现runnable接口,threadPoolTaskScheduler需要接受一个runnable的实现类)

package com.syx.task;
public class HelloTask implements Runnable{
    @Override
    public void run() {
        System.out.println("hello world");
    }
}

4、ScheduledFutureHolder自定义的一个辅助类,对任务执行进行包装

package com.syx.entity;

import java.util.concurrent.ScheduledFuture;

/**
 * 任务执行的包装类
 */
public class ScheduledFutureHolder {
    private ScheduledFuture<?> scheduledFuture;

    private Class<? extends Runnable> runnableClass;

    private String corn;

    public ScheduledFuture<?> getScheduledFuture() {
        return scheduledFuture;
    }

    public void setScheduledFuture(ScheduledFuture<?> scheduledFuture) {
        this.scheduledFuture = scheduledFuture;
    }

    public Class<? extends Runnable> getRunnableClass() {
        return runnableClass;
    }

    public void setRunnableClass(Class<? extends Runnable> runnableClass) {
        this.runnableClass = runnableClass;
    }

    public String getCorn() {
        return corn;
    }

    public void setCorn(String corn) {
        this.corn = corn;
    }

    @Override
    public String toString() {
        return "ScheduledFutureHolder{" +
                "scheduledFuture=" + scheduledFuture +
                ", runnableClass=" + runnableClass +
                ", corn='" + corn + '\'' +
                '}';
    }
}

5、启动类(将EnableScheduling注解去掉,不去也行)

package com.syx;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
//@EnableScheduling
public class MainApp {
    public static void main(String[] args) {
        SpringApplication.run(MainApp.class,args);
    }
}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐