• Spring的SchedulingConfigurer实现定时任务


    前提:在做业务平台的时候我们经常会遇到,某些跟时间打交道的需要修改状态,比如说在时间区间之前,属于未生效状态,区间之内属于有效期,区间之后,属于过期,或者需要每天 每周 每月,甚至是年为单位的做一些固定的操作。通过定时任务可以通过开启定时任务来完成这些需求。

    我做合同管理模块,合同有未生效,已生效,已过期,三个状态,不可能每次用户登录的时候去判断这个状态,然后修改,这样做会在登录的逻辑里边耦合了合同业务逻辑,同时消耗了登录时间,不太可取。

    下便是代码:

    依赖:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.1.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.xc</groupId>
        <artifactId>timetask</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>timetask</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.0.0</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>commons-lang</groupId>
                <artifactId>commons-lang</artifactId>
                <version>2.6</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    pom.xml

    主启动类开启定时任务注解

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.scheduling.annotation.EnableScheduling;
    import org.springframework.web.bind.annotation.RestController;
    
    @SpringBootApplication
    @RestController
    @EnableScheduling
    public class TimetaskApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(TimetaskApplication.class, args);
        }
    
    }

    SpringUtil工具类:需要ApplicationContextAware接口

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    @Component
    public class SpringUtil implements ApplicationContextAware {
    
        private Logger logger= LoggerFactory.getLogger(SpringUtil.class);
    
        private static ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            if(SpringUtil.applicationContext == null) {
                SpringUtil.applicationContext = applicationContext;
            }
            logger.info("========ApplicationContext配置成功,在普通类可以通过调用SpringUtils.getAppContext()获取applicationContext对象,applicationContext="+SpringUtil.applicationContext+"========");
        }
    
        //获取applicationContext
        public static ApplicationContext getApplicationContext() {
            return applicationContext;
        }
    
        //通过name获取 Bean.
        public  static Object getBean(String name){
            Class cla = null;
            try {
                cla=Class.forName(name);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return getApplicationContext().getBean(cla);
        }
    
        //通过class获取Bean.
        public  static <T> T getBean(Class<T> clazz){
            return getApplicationContext().getBean(clazz);
        }
    
        //通过name,以及Clazz返回指定的Bean
        public  static <T> T getBean(String name,Class<T> clazz){
            return getApplicationContext().getBean(name, clazz);
        }
    
    
    
    }
    SpringUtil.java

    SysTaskController实现SchedulingConfigurer接口,配置定时任务以及开启定时任务

    import com.xc.timetask.entity.Task;
    import com.xc.timetask.service.TaskService;
    import com.xc.timetask.util.SpringUtil;
    import org.apache.commons.lang.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.scheduling.Trigger;
    import org.springframework.scheduling.TriggerContext;
    import org.springframework.scheduling.annotation.SchedulingConfigurer;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
    import org.springframework.scheduling.config.ScheduledTaskRegistrar;
    import org.springframework.scheduling.support.CronTrigger;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.ScheduledFuture;
    
    @Lazy(value = false)
    @Component
    public class SysTaskController implements SchedulingConfigurer {
    
        protected static Logger logger = LoggerFactory.getLogger(SysTaskController.class);
    
        private SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        private static ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
    
        private static Map<String,ScheduledFuture<?>> scheduledFutureMap = new HashMap<>();
    
        @Resource
        private TaskService taskService;
    
        //从数据库里取得所有要执行的定时任务
        private List<Task> getAllTasks() throws Exception {
            List<Task> list=taskService.selectyunx();
            return list;
        }
        static {
            threadPoolTaskScheduler.initialize();
        }
        @Override
        public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
            List<Task> tasks= null;
            try {
                tasks = getAllTasks();
            } catch (Exception e) {
                e.printStackTrace();
            }
            logger.info("定时任务启动,预计启动任务数量="+tasks.size()+"; time="+sdf.format(new Date()));
    
            //校验数据(这个步骤主要是为了打印日志,可以省略)
            checkDataList(tasks);
    
            //通过校验的数据执行定时任务
            int count = 0;
            if(tasks.size()>0) {
                for (Task task:tasks) {
                    try {
                        //taskRegistrar.addTriggerTask(getRunnable(task), getTrigger(task));
                        start(task);
                        count++;
                    } catch (Exception e) {
                        logger.error("定时任务启动错误:" + task.getBean_name() + ";" + task.getMethod_name() + ";" + e.getMessage());
                    }
                }
            }
            logger.info("定时任务实际启动数量="+count+"; time="+sdf.format(new Date()));
        }
        private static Runnable getRunnable(Task task){
            return new Runnable() {
                @Override
                public void run() {
                    try {
                        Object obj = SpringUtil.getBean(task.getBean_name());
                        Method method = obj.getClass().getMethod(task.getMethod_name());
                        method.invoke(obj);
                    } catch (InvocationTargetException e) {
                        logger.error("定时任务启动错误,反射异常:"+task.getBean_name()+";"+task.getMethod_name()+";"+ e.getMessage());
                    } catch (Exception e) {
                        logger.error(e.getMessage());
                    }
                }
            };
        }
    
    
        private static Trigger getTrigger(Task task){
            return new Trigger() {
                @Override
                public Date nextExecutionTime(TriggerContext triggerContext) {
                    //将Cron 0/1 * * * * ? 输入取得下一次执行的时间
                    CronTrigger trigger = new CronTrigger(task.getCron());
                    Date nextExec = trigger.nextExecutionTime(triggerContext);
                    return nextExec;
                }
            };
    
        }
    
        private List<Task> checkDataList(List<Task> list) {
            String errMsg="";
            for(int i=0;i<list.size();i++){
                if(!checkOneData(list.get(i)).equalsIgnoreCase("success")){
                    errMsg+=list.get(i).getName()+";";
                    list.remove(list.get(i));
                    i--;
                };
            }
            if(!StringUtils.isBlank(errMsg)){
                errMsg="未启动的任务:"+errMsg;
                logger.error(errMsg);
            }
            return list;
        }
    
        public static String checkOneData(Task task){
            String result="success";
            Class cal= null;
            try {
                cal = Class.forName(task.getBean_name());
    
                Object obj = SpringUtil.getBean(cal);
                Method method = obj.getClass().getMethod(task.getMethod_name());
                String cron=task.getCron();
                if(StringUtils.isBlank(cron)){
                    result="定时任务启动错误,无cron:"+task.getName();
                    logger.error(result);
                }
            } catch (ClassNotFoundException e) {
                result="定时任务启动错误,找不到类:"+task.getBean_name()+ e.getMessage();
                logger.error(result);
            } catch (NoSuchMethodException e) {
                result="定时任务启动错误,找不到方法,方法必须是public:"+task.getBean_name()+";"+task.getMethod_name()+";"+ e.getMessage();
                logger.error(result);
            } catch (Exception e) {
                logger.error(e.getMessage());
            }
            return result;
        }
        /**
         * 启动定时任务
         * @param task
         * @param
         */
        public static void start(Task task){
    
            ScheduledFuture<?> scheduledFuture = threadPoolTaskScheduler.schedule(getRunnable(task),getTrigger(task));
            scheduledFutureMap.put(task.getId(),scheduledFuture);
            logger.info("启动定时任务" + task.getId() );
    
        }
    
        /**
         * 取消定时任务
         * @param task
         */
        public static void cancel(Task task){
    
            ScheduledFuture<?> scheduledFuture = scheduledFutureMap.get(task.getId());
    
            if(scheduledFuture != null && !scheduledFuture.isCancelled()){
                scheduledFuture.cancel(Boolean.FALSE);
            }
    
            scheduledFutureMap.remove(task.getId());
            logger.info("取消定时任务" + task.getId() );
    
        }
    
        /**
         * 编辑
         * @param task
         * @param
         */
        public static void reset(Task task){
            logger.info("修改定时任务开始" + task.getId() );
            cancel(task);
            start(task);
            logger.info("修改定时任务结束" + task.getId());
        }
    
    }
    SysTaskController.java

    TaskDemo:定时任务要操作的类

    import org.springframework.stereotype.Component;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    @Component
    public class TaskDemo {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        //注入需要的service
        //private ContractService contractService;
    
        public String run(){
    
            try {
                /*
                使用service做一些修改的业务操作
                获取所有的合同列表
                List<Contract> list = contractService.getAllContract();
                修改合同的状态
                contractService.updateContractList(list);
    
                下边用打印***来代替业务进行测试定时任务
                 */
                System.out.println("*********");
                
                System.out.println("TaskContract is running"+sdf.format(new Date()));
            } catch (Exception e) {
                e.printStackTrace();
            }
            return "/main";
        }
    }
    TaskDemo.java

    其他的controller service dao类就省略了,后边会给出github地址,有demo可执行

    演示及说明 

    访问/timetask/list可以看到所有的定时任务列表  可以进行启动  停止  以及常规的增删改查

     特别说明 新增的时候类名必须是全路径类名,因为是通过Class.forName("com.xc.timetask········") 反射来加载要执行的类

    表达式    是cron表达式  就是定时任务的执行单位  不知道的可以 戳这里 去看看

    方法  是要执行的方法的名字

    新增和修改页面  方法哪里填写 run 就可以 和 TaskDemo 里的那个方法对应

     这里忘记加上时间区间的datepicker日历控件 来进行开始时间 结束时间填充, 直接去数据库表里修改开始 结束时间   轻点喷我!!!!!

    另外demo使用了LayUI   真的是惨不忍睹  弹出层会重复 -!-  不过后台代码都是好用的

    结果  因为上边图中列表  只有一个定时任务开启的

    需要demo 源码的  请戳这里

    SysTaskController
  • 相关阅读:
    MySQL Partition--分区基础
    MySQL Replication--跳过复制错误
    MySQL--SHOW PROCESSLIST
    MySQL InnoDB Engine--缓冲器数据交换
    MySQL InnoDB Engine--数据预热
    MySQL Profiling--常用命令
    Linux--用户管理
    vi和vim快捷键的使用
    vi和vim
    xftp使用
  • 原文地址:https://www.cnblogs.com/xuchao0506/p/13301793.html
Copyright © 2020-2023  润新知