springboot+Quartz开机启动定时任务,cron可配置

之前写过一篇spring整合quartz的定时任务调度,主要通过XML文件来实现任务相关的配置,比较繁琐。现在由于公司使用springboot,加上碰到实现定时任务的需求,因此简单记录下我在项目中用springboot实现定时任务的方式。

springboot有俩种实现定时任务的方式,第一种就是spring自带的俩个定时任务注解@EnableScheduling(用于配置类上)与@Scheduled(用于定时任务方法上)具体用法如下:

package com.tycho.readserialport.schedule;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * springboot自带的schedule定时任务配置方式
 * ll 2020年6月08日 ✿(。◕ᴗ◕。)✿  
 */
@Configuration
@EnableScheduling
@Slf4j
public class ScheduleTask {
	
	
    @Scheduled(cron = "0 0 0 */1 * ?")
    private void configureTasks() {
    	log.info("你你你你!!!1");
    }
    
    /**
     * 检测获取设备状态
     */
    @Scheduled(cron = "0 1/1 1/1 1/1 * ?")
    private void detectDeviceStatus() {
    	log.info("你你你你!!!1");
    }
    
    
}

执行结果:
执行结果
第二种就是整合quartz来新建和管理定时任务

  1. 首先引入jar包,版本统一由springboot-starter-parent管理
 <dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-quartz</artifactId>
		</dependency>
  1. 编写配置类
    工厂类
package com.tycho.readserialport.schedule;

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
@Component
public class QuartzJobBeanFactory extends AdaptableJobFactory  implements ApplicationContextAware {

    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    public void setApplicationContext(final ApplicationContext context) throws BeansException {
    	capableBeanFactory = context.getAutowireCapableBeanFactory();
    }
    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //调用父类的方法
        Object jobInstance = super.createJobInstance(bundle);
        //进行注入
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }

}

注入工厂对象及任务调度对象

package com.tycho.readserialport.config;


import org.quartz.Scheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import com.tycho.readserialport.schedule.QuartzJobBeanFactory;
 
@Configuration
public class QuartzConfig {
 
	@Bean
	public QuartzJobBeanFactory autoWiringSpringBeanJobFactory() {
		return new QuartzJobBeanFactory();
	}
	@Bean
	public SchedulerFactoryBean schedulerFactoryBean() {
		SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
		schedulerFactoryBean.setJobFactory(autoWiringSpringBeanJobFactory());
		return schedulerFactoryBean;
	}
	@Bean
	public Scheduler scheduler() {
		return schedulerFactoryBean().getScheduler();
	}
}

封装任务相关方法

package com.tycho.readserialport.schedule;

import java.util.Map;

import org.quartz.CronScheduleBuilder;  
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;  
import org.quartz.JobKey;  
import org.quartz.Scheduler;  
import org.quartz.SchedulerException;  
import org.quartz.SchedulerFactory;  
import org.quartz.Trigger;  
import org.quartz.TriggerBuilder;  
import org.quartz.TriggerKey;  
import org.quartz.impl.StdSchedulerFactory;
  
/** 
 1.  
 2. @Description 定时任务管理类 负责增加、删除、启动、暂停定时任务
 */  
public class QuartzManager {  
    private static SchedulerFactory gSchedulerFactory = new StdSchedulerFactory();    
    private static String JOB_GROUP_NAME = "查询设备状态";    
    private static String TRIGGER_GROUP_NAME = "查询设备状态触发器";    
    
    /** 
     * @Description: 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名 
     * @param jobName 任务名 
     * @param cls 任务 
     * @param time 时间设置,参考quartz说明文档  
     * ll 2020年6月08日 ✿(。◕ᴗ◕。)✿   
     */  
    @SuppressWarnings({ "unchecked", "rawtypes" })    
    public static void addJob(String jobName, Class cls, String time,Map<String,Object> params) {    
        try {    
            Scheduler sched = gSchedulerFactory.getScheduler();    
            JobDetail job = JobBuilder.newJob(cls)  
                    .withIdentity(jobName, JOB_GROUP_NAME)  
                    .build();  
            // 添加具体任务方法  
            job.getJobDataMap().putAll(params);
            // 表达式调度构建器  
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(time);  
            // 按新的cronExpression表达式构建一个新的trigger  
            Trigger trigger = TriggerBuilder  
                    .newTrigger()  
                    .withIdentity(jobName, TRIGGER_GROUP_NAME)  
                            .withSchedule(scheduleBuilder).build();  
  
            //交给scheduler去调度  
            sched.scheduleJob(job, trigger);  
              
            // 启动    
            if (!sched.isShutdown()) {    
                sched.start();    
            }    
        } catch (Exception e) {    
            throw new RuntimeException(e);    
        }    
    }    
    /** 
     * @Description: 添加一个定时任务  
     * @param jobName 任务名 
     * @param jobGroupName 任务组名 
     * @param triggerName 触发器名  
     * @param triggerGroupName 触发器组名 
     * @param jobClass 任务 
     * @param time 时间设置,参考quartz说明文档 
     * ll 2020年6月08日 ✿(。◕ᴗ◕。)✿  
     */  
    @SuppressWarnings({ "rawtypes", "unchecked" })
	public static void addJob(String jobName, String jobGroupName,    
            String triggerName, String triggerGroupName, Class jobClass,    
            String time) {    
        try {    
            Scheduler sched = gSchedulerFactory.getScheduler();    
            JobDetail job = JobBuilder.newJob(jobClass)  
                    .withIdentity(jobName, jobGroupName)  
                    .build();  
            // 表达式调度构建器  
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(time);  
            // 按新的cronExpression表达式构建一个新的trigger  
            Trigger trigger = TriggerBuilder  
                    .newTrigger()  
                    .withIdentity(triggerName, triggerGroupName)  
                            .withSchedule(scheduleBuilder).build();  
            sched.scheduleJob(job, trigger);   
            // 启动    
            if (!sched.isShutdown()) {    
                sched.start();    
            }    
        } catch (Exception e) {    
            throw new RuntimeException(e);    
        }    
    }    
    
    /** 
     * @Description: 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名)  
     * @param jobName 
     * @param time 
     * ll 2020年6月08日 ✿(。◕ᴗ◕。)✿   
     */  
    public static void modifyJobTime(String jobName, String time) {   
        TriggerKey triggerKey = TriggerKey.triggerKey(  
                jobName, TRIGGER_GROUP_NAME);  
          
        try {    
            Scheduler sched = gSchedulerFactory.getScheduler();    
            CronTrigger trigger =(CronTrigger) sched.getTrigger(triggerKey);  
            if (trigger == null) {    
                return;    
            }    
            String oldTime = trigger.getCronExpression();    
            if (!oldTime.equalsIgnoreCase(time)) {  
                CronScheduleBuilder scheduleBuilder =CronScheduleBuilder.cronSchedule(time);  
                //按新的cronExpression表达式重新构建trigger  
                trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)  
                .withSchedule(scheduleBuilder).build();  
                //按新的trigger重新设置job执行  
                sched.rescheduleJob(triggerKey, trigger);  
            }    
        } catch (Exception e) {    
            throw new RuntimeException(e);    
        }    
    }    
    
    /** 
     * @Description: 修改一个任务的触发时间  
     * @param triggerName 
     * @param triggerGroupName 
     * @param time 
     * @author ll  
     * @date ll 2020年6月08日 ✿(。◕ᴗ◕。)✿   
     */  
    public static void modifyJobTime(String triggerName,    
            String triggerGroupName, String time) {   
        TriggerKey triggerKey = TriggerKey.triggerKey(  
                triggerName, triggerGroupName);  
        try {    
            Scheduler sched = gSchedulerFactory.getScheduler();    
            CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);    
            if (trigger == null) {    
                return;    
            }    
            String oldTime = trigger.getCronExpression();    
            if (!oldTime.equalsIgnoreCase(time)) {    
                // trigger已存在,则更新相应的定时设置  
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder  
              .cronSchedule(time);  
                // 按新的cronExpression表达式重新构建trigger  
                trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)  
                        .withSchedule(scheduleBuilder).build();  
                // 按新的trigger重新设置job执行  
                sched.resumeTrigger(triggerKey);  
            }    
        } catch (Exception e) {    
            throw new RuntimeException(e);    
        }    
    }    
    
    /** 
     * @Description 移除一个任务(使用默认的任务组名,触发器名,触发器组名) 
     * @param jobName 
     * @author ll  
     * @date ll 2020年6月08日 ✿(。◕ᴗ◕。)✿   
     */  
    public static void removeJob(String jobName) {   
        TriggerKey triggerKey = TriggerKey.triggerKey(  
                jobName, TRIGGER_GROUP_NAME);  
        JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP_NAME);  
        try {    
            Scheduler sched = gSchedulerFactory.getScheduler();  
            Trigger trigger = (Trigger) sched.getTrigger(triggerKey);    
            if (trigger == null) {    
                return;    
            }  
            sched.pauseTrigger(triggerKey);;// 停止触发器    
            sched.unscheduleJob(triggerKey);// 移除触发器    
            sched.deleteJob(jobKey);// 删除任务    
        } catch (Exception e) {    
            throw new RuntimeException(e);    
        }    
    }    
    
    /**  
     * @Description: 移除一个任务  
     * @param jobName  
     * @param jobGroupName  
     * @param triggerName  
     * @param triggerGroupName  
     * @author ll  
     * @date ll 2020年6月08日 ✿(。◕ᴗ◕。)✿   
     */    
    public static void removeJob(String jobName, String jobGroupName,    
            String triggerName, String triggerGroupName) {   
        TriggerKey triggerKey = TriggerKey.triggerKey(  
                jobName, triggerGroupName);  
        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);  
        try {    
            Scheduler sched = gSchedulerFactory.getScheduler();   
            sched.pauseTrigger(triggerKey);// 停止触发器    
            sched.unscheduleJob(triggerKey);// 移除触发器    
            sched.deleteJob(jobKey);// 删除任务  
        } catch (Exception e) {    
            throw new RuntimeException(e);    
        }    
    }   
    /** 
     * @Description:暂停一个任务 
     * @param jobName 
     * @param jobGroupName 
     * ll 2020年6月08日 ✿(。◕ᴗ◕。)✿   
     */  
    public static void pauseJob(String jobName, String jobGroupName) {  
        JobKey jobKey =JobKey.jobKey(jobName, jobName);  
        try {  
            Scheduler sched = gSchedulerFactory.getScheduler();  
            sched.pauseJob(jobKey);  
        } catch (SchedulerException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
    /** 
     * @Description:暂停一个任务(使用默认组名) 
     * @param jobName 
     * @param jobGroupName 
     * ll 2020年6月08日 ✿(。◕ᴗ◕。)✿   
     */  
    public static void pauseJob(String jobName) {  
        JobKey jobKey =JobKey.jobKey(jobName, JOB_GROUP_NAME);  
        try {  
            Scheduler sched = gSchedulerFactory.getScheduler();  
            sched.pauseJob(jobKey);  
        } catch (SchedulerException e) {  
            e.printStackTrace();  
        }  
    }  
    /**  
     * @Description:启动所有定时任务  
     * @author ll 
     * @date ll 2020年6月08日 ✿(。◕ᴗ◕。)✿   
     */  
    public static void startJobs() {    
        try {    
            Scheduler sched = gSchedulerFactory.getScheduler();    
            sched.start();    
        } catch (Exception e) {    
            throw new RuntimeException(e);    
        }    
    }    
    
    /** 
     * @Description 关闭所有定时任务  
     * @author qgw  
     * @date ll 2020年6月08日 ✿(。◕ᴗ◕。)✿   
     */  
    public static void shutdownJobs() {    
        try {    
            Scheduler sched = gSchedulerFactory.getScheduler();    
            if (!sched.isShutdown()) {    
                sched.shutdown();    
            }    
        } catch (Exception e) {    
            throw new RuntimeException(e);    
        }    
    }   
     
}

定时任务的增删停止,此处添加定时任务方法都是通过传入自己实现的Job类,然后反射创建对象,构造JobDetail(同一个Job构造的是不同的JobDetail),最后用schedule调度任务
3. 接下来,使用自己定义的任务方法
新增一个定时任务需要实现Job接口

package com.tycho.readserialport.schedule;

import java.util.Date;

import org.quartz.DisallowConcurrentExecution;  
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;  
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import lombok.extern.slf4j.Slf4j;


/**
 * 定时任务类
 * 读站号状态任务
 * @author ll
 * 2020年6月08日 ✿(。◕ᴗ◕。)✿
 */
@Component
//保证同一个Jobdetail不会并发执行
@DisallowConcurrentExecution
@Slf4j
public class DetectDeviceStatusJob implements Job {  
    
    @Override  
    public void execute(JobExecutionContext context) throws JobExecutionException { 
    	//解决spring bean无法自动注入 (@Autowired注入失效问题)
        //解决不了就使用工具类获取IOC中的bean对象SpringContextUtils.getBean("");    	
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
    	//JobDataMap获取任务中传递的数据,实现了Map接口
    	JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
    	String cron = jobDataMap.getString("cron");
    	//获取开启任务时设置的withIdentity
    	//JobKey jobKey = context.getJobDetail().getKey();
        log.info("启动读取站号状态任务-----当前时间:{},当前线程:{},当前cron:{}",new Date(),Thread.currentThread().getName(),cron);   
        try {  
            //ScheduleJob任务运行时具体参数,可自定义  
        }catch (Exception e) {  
            log.error("读取站号状态任务运行异常:{}",e);  
        }  
    }  
}

@DisallowConcurrentExecution,因为定时任务job最后要通过JobDetail来运行,此注解用来保证同一个JobDetail对象不会同时运行(相同的任务可以同时运行),JobDataMap可以用来获取传给定时任务的参数,如果你的自定义Job注入的Ioc中对象,则需要SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);来保证注入成功(quartz整合产生的问题,具体自查),JobKey用来获取JobDetail中设置的 withIdentity

  1. 开机启动自定义的定时任务DetectDeviceStatusJob
package com.tycho.readserialport;

import java.time.Instant;
import java.util.HashMap;
import java.util.Map;

import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.Environment;

import com.tycho.readserialport.schedule.DetectDeviceStatusJob;
import com.tycho.readserialport.schedule.QuartzManager;

import lombok.extern.slf4j.Slf4j;

/**
 * 项目启动成功监听类
 * @author ll
 *
 */
@Slf4j
public class StartListener implements ApplicationListener<ApplicationStartedEvent>{


	 @Override
	public void onApplicationEvent(ApplicationStartedEvent event) {
		//项目启动,开启读站号状态定时任务
		Environment env = event.getApplicationContext().getEnvironment();
		String cron = env.getProperty("myconfig.job.cron");
		log.info("程序启动" + Instant.now());
		Map<String,Object> param = new HashMap<>();
		param.put("cron", cron);
		QuartzManager.addJob("读站号状态1", DetectDeviceStatusJob.class, cron,param);
		QuartzManager.addJob("读站号状态2", DetectDeviceStatusJob.class, cron,param);
		QuartzManager.addJob("读站号状态3", DetectDeviceStatusJob.class, cron,param);
		QuartzManager.addJob("读站号状态4", DetectDeviceStatusJob.class, cron,param);
	}
}

server:
  port:  8081
myconfig:
  serial:
    #采集数据串口
    data-port: COM1,COM2,COM3,COM4
    #进线信号串口
    incoming: COM5
    #波特率
    baud: 115200
  job:
    #配置定时任务执行周期
    cron: 0 1/1 1/1 1/1 * ?

通过实现ApplicationListener接口,监听项目的启动,在重写的方法中开启定时任务,此处的Cron时间表达式可通过Yml属性配置文件配置,通过环境配置类Environment 获得属性配置,param可给定时任务传递参数,通过JobDataMap获取
5. 运行
6.
关于Quartz线程池可添加配置文件quartz.properties自行定义,也可采用默认的线程池
总结Quartz与schedule异同

1. schedule比较简单,自动开机启动,quartz需要借助实现ApplicationListener监听项目启动来开启定时任务。
2. schedule与quartz的cron都可以通过配置文件来配置(上文schedule可用过@Value注解获取配置文件值)
3. schedule不能传参,quartz可以传入参数
4. schedule开机启动(动态开启需借助ThreadPoolTaskScheduler.schedule()实现),quartz可根据项目运行动态开启定时任务

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值