• Spring Boot定时任务应用实践


    在Spring Boot中实现定时任务功能,可以通过Spring自带的定时任务调度,也可以通过集成经典开源组件Quartz实现任务调度。

    一、Spring定时器

    1、cron表达式方式

    使用自带的定时任务,非常简单,只需要像下面这样,加上注解就好,不需要像普通定时任务框架那样继承任何定时处理接口 ,简单示例代码如下:

    复制代码
    package com.power.demo.scheduledtask.simple;
    
    import com.power.demo.util.DateTimeUtil;
    import org.springframework.scheduling.annotation.EnableScheduling;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    @Component
    @EnableScheduling
    public class SpringTaskA {
    
        /**
         * CRON表达式参考:http://cron.qqe2.com/
         **/
        @Scheduled(cron = "*/5 * * * * ?", zone = "GMT+8:00")
        private void timerCron() {
    
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            System.out.println(String.format("(timerCron)%s 每隔5秒执行一次,记录日志", DateTimeUtil.fmtDate(new Date())));
    
        }
    
    }
    复制代码

    上述代码中,在一个类上添加@EnableScheduling注解,在方法上加上@Scheduled,配置下cron表达式,一个最最简单的cron定时任务就完成了。cron表达式的各个组成部分,可以参考下面:

    @Scheduled(cron = "[Seconds] [Minutes] [Hours] [Day of month] [Month] [Day of week] [Year]")

    2、fixedRate和fixedDelay

    @Scheduled注解除了cron表达式,还有其他配置方式,比如fixedRate和fixedDelay,下面这个示例通过配置方式的不同,实现不同形式的定时任务调度,示例代码如下:

    复制代码
    package com.power.demo.scheduledtask.simple;
    
    import com.power.demo.util.DateTimeUtil;
    import org.springframework.scheduling.annotation.EnableScheduling;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    @Component
    @EnableScheduling
    public class SpringTaskB {
    
        /*fixedRate:上一次开始执行时间点之后5秒再执行*/
        @Scheduled(fixedRate = 5000)
        public void timerFixedRate() {
    
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            System.out.println(String.format("(fixedRate)现在时间:%s", DateTimeUtil.fmtDate(new Date())));
        }
    
        /*fixedDelay:上一次执行完毕时间点之后5秒再执行*/
        @Scheduled(fixedDelay = 5000)
        public void timerFixedDelay() {
    
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            System.out.println(String.format("(fixedDelay)现在时间:%s", DateTimeUtil.fmtDate(new Date())));
    
        }
    
        /*第一次延迟2秒后执行,之后按fixedDelay的规则每5秒执行一次*/
        @Scheduled(initialDelay = 2000, fixedDelay = 5000)
        public void timerInitDelay() {
    
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            System.out.println(String.format("(initDelay)现在时间:%s", DateTimeUtil.fmtDate(new Date())));
        }
    
    }
    复制代码

    注意一下主要区别:

    @Scheduled(fixedRate = 5000) :上一次开始执行时间点之后5秒再执行

    @Scheduled(fixedDelay = 5000) :上一次执行完毕时间点之后5秒再执行

    @Scheduled(initialDelay=2000, fixedDelay=5000) :第一次延迟2秒后执行,之后按fixedDelay的规则每5秒执行一次

    有时候,很多项目我们都需要配置好定时任务后立即执行一次,initialDelay就可以不用配置了。

    3、zone

    @Scheduled注解还有一个熟悉的属性zone,表示时区,通常,如果不写,定时任务将使用服务器的默认时区;如果你的任务想在特定时区特定时间点跑起来,比如常见的多语言系统可能会定时跑脚本更新数据,就可以设置一个时区,如东八区,就可以设置为:

    zone = "GMT+8:00"

    二、Quartz

    Quartz是应用最为广泛的开源任务调度框架之一,有很多公司都根据它实现自己的定时任务管理系统。Quartz提供了最常用的两种定时任务触发器,即SimpleTrigger和CronTrigger,本文以最广泛使用的CronTrigger为例。

    1、添加依赖

            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
                <version>2.3.0</version>
            </dependency>

    2、配置cron表达式

    示例代码需要,在application.properties文件中新增如下配置:

     cron-config

    其实,我们完全可以不用配置,直接在代码里面写或者持久化在DB中然后读取也可以。

    3、添加定时任务实现

    任务1:

    复制代码
    package com.power.demo.scheduledtask.quartz;
    
    import com.power.demo.util.DateTimeUtil;
    import org.quartz.DisallowConcurrentExecution;
    import org.quartz.Job;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    
    import java.util.Date;
    
    
    @DisallowConcurrentExecution
    public class QuartzTaskA implements Job {
    
        @Override
        public void execute(JobExecutionContext var1) throws JobExecutionException {
            try {
                Thread.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            System.out.println(String.format("(QuartzTaskA)%s 每隔3秒执行一次,记录日志", DateTimeUtil.fmtDate(new Date())));
    
        }
    }
    复制代码

    任务2:

     QuartzTaskB

    定时发送邮件任务:

     MailSendTask

    实现任务看上去非常简单,继承Quartz的Job接口,重写execute方法即可。

    4、集成Quartz定时任务

    怎么让Spring自动识别初始化Quartz定时任务实例呢?这就需要引用Spring管理的Bean,向Spring容器暴露所必须的bean,通过定义Job Factory实现自动注入。

    首先,添加Spring注入的Job Factory类:

    复制代码
    package com.power.demo.scheduledtask.quartz.config;
    
    import org.quartz.spi.TriggerFiredBundle;
    import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.scheduling.quartz.SpringBeanJobFactory;
    
    public final class AutowireBeanJobFactory extends SpringBeanJobFactory
            implements ApplicationContextAware {
    
        private transient AutowireCapableBeanFactory beanFactory;
    
        /**
         * Spring提供了一种机制让你可以获取ApplicationContext,即ApplicationContextAware接口
         * 对于一个实现了ApplicationContextAware接口的类,Spring会实例化它的同时调用它的
         * public voidsetApplicationContext(ApplicationContext applicationContext) throws BeansException;接口,
         * 将该bean所属上下文传递给它。
         **/
        @Override
        public void setApplicationContext(final ApplicationContext context) {
            beanFactory = context.getAutowireCapableBeanFactory();
        }
    
        @Override
        protected Object createJobInstance(final TriggerFiredBundle bundle)
                throws Exception {
            final Object job = super.createJobInstance(bundle);
            beanFactory.autowireBean(job);
            return job;
        }
    }
    复制代码

    定义QuartzConfig:

    复制代码
    package com.power.demo.scheduledtask.quartz.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
    import org.springframework.scheduling.quartz.SchedulerFactoryBean;
    
    @Configuration
    public class QuartzConfig {
    
        @Autowired
        @Qualifier("quartzTaskATrigger")
        private CronTriggerFactoryBean quartzTaskATrigger;
    
        @Autowired
        @Qualifier("quartzTaskBTrigger")
        private CronTriggerFactoryBean quartzTaskBTrigger;
    
        @Autowired
        @Qualifier("mailSendTrigger")
        private CronTriggerFactoryBean mailSendTrigger;
    
        //Quartz中的job自动注入spring容器托管的对象
        @Bean
        public AutowireBeanJobFactory autoWiringSpringBeanJobFactory() {
            return new AutowireBeanJobFactory();
        }
    
        @Bean
        public SchedulerFactoryBean schedulerFactoryBean() {
            SchedulerFactoryBean scheduler = new SchedulerFactoryBean();
            scheduler.setJobFactory(autoWiringSpringBeanJobFactory());  //配置Spring注入的Job类
    
            //设置CronTriggerFactoryBean,设定任务Trigger
            scheduler.setTriggers(
                    quartzTaskATrigger.getObject(),
                    quartzTaskBTrigger.getObject(),
                    mailSendTrigger.getObject()
            );
            return scheduler;
        }
    
    }
    复制代码

    接着配置job明细:

    复制代码
    package com.power.demo.scheduledtask.quartz.config;
    
    import com.power.demo.common.AppField;
    import com.power.demo.scheduledtask.quartz.MailSendTask;
    import com.power.demo.scheduledtask.quartz.QuartzTaskA;
    import com.power.demo.scheduledtask.quartz.QuartzTaskB;
    import com.power.demo.util.ConfigUtil;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
    import org.springframework.scheduling.quartz.JobDetailFactoryBean;
    
    @Configuration
    public class TaskSetting {
    
        @Bean(name = "quartzTaskA")
        public JobDetailFactoryBean jobDetailAFactoryBean() {
            //生成JobDetail
            JobDetailFactoryBean factory = new JobDetailFactoryBean();
            factory.setJobClass(QuartzTaskA.class);  //设置对应的Job
            factory.setGroup("quartzTaskGroup");
            factory.setName("quartzTaskAJob");
            factory.setDurability(false);
            factory.setDescription("测试任务A");
    
            return factory;
        }
    
        @Bean(name = "quartzTaskATrigger")
        public CronTriggerFactoryBean cronTriggerAFactoryBean() {
    
            String cron = ConfigUtil.getConfigVal(AppField.JOB_TASKA_CRON);
    
            CronTriggerFactoryBean stFactory = new CronTriggerFactoryBean();
            //设置JobDetail
            stFactory.setJobDetail(jobDetailAFactoryBean().getObject());
            stFactory.setStartDelay(1000);
            stFactory.setName("quartzTaskATrigger");
            stFactory.setGroup("quartzTaskGroup");
            stFactory.setCronExpression(cron);
    
            return stFactory;
        }
    
        @Bean(name = "quartzTaskB")
        public JobDetailFactoryBean jobDetailBFactoryBean() {
            //生成JobDetail
            JobDetailFactoryBean factory = new JobDetailFactoryBean();
            factory.setJobClass(QuartzTaskB.class);  //设置对应的Job
            factory.setGroup("quartzTaskGroup");
            factory.setName("quartzTaskBJob");
            factory.setDurability(false);
            factory.setDescription("测试任务B");
    
            return factory;
        }
    
        @Bean(name = "quartzTaskBTrigger")
        public CronTriggerFactoryBean cronTriggerBFactoryBean() {
    
            String cron = ConfigUtil.getConfigVal(AppField.JOB_TASKB_CRON);
    
            CronTriggerFactoryBean stFactory = new CronTriggerFactoryBean();
            //设置JobDetail
            stFactory.setJobDetail(jobDetailBFactoryBean().getObject());
            stFactory.setStartDelay(1000);
            stFactory.setName("quartzTaskBTrigger");
            stFactory.setGroup("quartzTaskGroup");
            stFactory.setCronExpression(cron);
    
            return stFactory;
        }
    
        @Bean(name = "mailSendTask")
        public JobDetailFactoryBean jobDetailMailFactoryBean() {
            //生成JobDetail
            JobDetailFactoryBean factory = new JobDetailFactoryBean();
            factory.setJobClass(MailSendTask.class);  //设置对应的Job
            factory.setGroup("quartzTaskGroup");
            factory.setName("mailSendTaskJob");
            factory.setDurability(false);
            factory.setDescription("邮件发送任务");
    
            return factory;
        }
    
        @Bean(name = "mailSendTrigger")
        public CronTriggerFactoryBean cronTriggerMailFactoryBean() {
    
            String cron = ConfigUtil.getConfigVal(AppField.JOB_TASKMAIL_CRON);
    
            CronTriggerFactoryBean stFactory = new CronTriggerFactoryBean();
            //设置JobDetail
            stFactory.setJobDetail(jobDetailMailFactoryBean().getObject());
            stFactory.setStartDelay(1000);
            stFactory.setName("mailSendTrigger");
            stFactory.setGroup("quartzTaskGroup");
            stFactory.setCronExpression(cron);
    
            return stFactory;
        }
    
    }
    复制代码

    最后启动你的Spring Boot定时任务应用,一个完整的基于Quartz调度的定时任务就实现好了。

    本文定时任务示例中,有一个定时发送邮件任务MailSendTask,下一篇将分享Spring Boot应用中以MongoDB作为存储介质的简易邮件系统。

    扩展阅读:

    很多公司都会有自己的定时任务调度框架和系统,在Spring Boot中如何整合Quartz集群,实现动态定时任务配置?

    参考:

    http://www.cnblogs.com/vincent0928/p/6294792.html

    http://www.cnblogs.com/zhenyuyaodidiao/p/4755649.html

  • 相关阅读:
    Kruskal算法
    拓扑排序
    邻接表有向图
    邻接矩阵的有向图
    邻接表无向图
    邻接矩阵无向图
    斐波那契堆
    二项堆
    斜堆(待补充)
    项目中maven依赖无法自动下载
  • 原文地址:https://www.cnblogs.com/itrena/p/8992614.html
Copyright © 2020-2023  润新知