springboot-quartz普通任务与可传参任务
两者区别与作用:
普通任务:总调度(SchedulerFactoryBean)--> 定时调度器(CronTriggerFactoryBean) --> 调度明细自定义执行方法bean(MethodInvokingJobDetailFactoryBean) -->调度bean(我们定义的job类)
可传参任务:总调度(SchedulerFactoryBean)--> 定时调度器(CronTriggerFactoryBean) --> 调度明细bean(JobDetailFactoryBean)
如上是我们在配置调度器时的具体步骤及相互之间的依赖,区别主要在调度明细bean上,普通任务是(MethodInvokingJobDetailFactoryBean),而可传参任务是(JobDetailFactoryBean):
1)普通任务可以自定义执行方法,也就是说在其配置的调度bean(我们定义的job类)中我们可以自定义调度器最终执行的方法,可以叫work1也可以叫work2,and so on。而且要记住的一点,必须是无入参的方法!!!(做了一个测试,测试目的是看通过此类型调度类型是否也能传入参数所以定义了一个带入参的自定义方法即public void work(JobExecutionContext jobExecutionContext),最中测试结果是spring quartz会报找不到work方法及No such method work(),其默认执行我们生命的work无入参的方法。
2)可传参任务必须继承QuartzJobBean,重写protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException方法,其中JobExecutionContext就是我们在定义调度器明细时传入参数的上下文,我们可以通过JobExecutionContext取出传入的map,调度任务最终执行的就是executeInternal方法,使用该调度明细任务无法使用自定义方法。
提供三种形式的定时任务:
1、固定等待时间 @Scheduled(fixedDelay = 时间间隔 )
2、固定间隔时间 @Scheduled(fixedRate = 时间间隔 )
3、Corn表达式 @Scheduled(cron = Corn表达式)
添加依赖(不知道为什么要加jboss-logging依赖,没有会报错)
<!-- 添加springboot对Quartz的支持 --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.jboss.logging/jboss-logging --> <dependency> <groupId>org.jboss.logging</groupId> <artifactId>jboss-logging</artifactId> </dependency>
配置quartz.properties
#===============================================================
#Configure Main Scheduler Properties 调度器属性
#===============================================================
#调度器的实例名
org.quartz.scheduler.instanceName = QuartzScheduler
#调度器的实例ID,大多数情况设置为auto即可
org.quartz.scheduler.instanceId = AUTO
#===============================================================
#Configure ThreadPool 线程池属性
#===============================================================
#处理Job的线程个数,至少为1,但最多的话最好不要超过100,在多数机器上设置该值超过100的话就会显得相当不实用了,特别是在你的 Job 执行时间较长的情况下
org.quartz.threadPool.threadCount = 5
#线程的优先级,优先级别高的线程比级别低的线程优先得到执行。最小为1,最大为10,默认为5
org.quartz.threadPool.threadPriority = 5
#一个实现了 org.quartz.spi.ThreadPool 接口的类,Quartz 自带的线程池实现类是 org.quartz.smpl.SimpleThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#===============================================================
#Configure JobStore 作业存储设置
#===============================================================
#要使 Job 存储在内存中需通过设置 org.quartz.jobStrore.class 属性为 org.quartz.simpl.RAMJobStore
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
一、普通任务
1、任务类
package cloud.app.prod.home.quartz.mem; import org.apache.log4j.Logger; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.stereotype.Component; import cloud.app.prod.home.common.FailException; /** * Author : YongBo Xie </br> * File Name: ScheduleTask.java </br> * Created Date: 2018年3月31日 下午3:37:43 </br> * Modified Date: 2018年3月31日 下午3:37:43 </br> * Version: 1.0 </br> */ @Configuration @Component // 此注解必加 @EnableScheduling // 此注解必加 public class ScheduleTask { private static Logger logger = Logger.getLogger(ScheduleTask.class); public void marketingActivity() throws FailException { logger.info("execute activity"); } }
2、Quartz配置类
package cloud.app.prod.home.quartz.mem; import org.quartz.Trigger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.CronTriggerFactoryBean; import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean; import org.springframework.scheduling.quartz.SchedulerFactoryBean; /** * Author : YongBo Xie </br> * File Name: QuartzConfigration.java </br> * Created Date: 2018年3月31日 下午3:42:04 </br> * Modified Date: 2018年3月31日 下午3:42:04 </br> * Version: 1.0 </br> */ @Configuration public class QuartzConfigration { /** * Details:配置定时任务 */ @Bean(name = "marketingActivityJobDetail") public MethodInvokingJobDetailFactoryBean detailFactoryBean(ScheduleTask task) {// ScheduleTask为需要执行的任务 MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean(); /* * 是否并发执行 * 例如每5s执行一次任务,但是当前任务还没有执行完,就已经过了5s了, * 如果此处为true,则下一个任务会执行,如果此处为false,则下一个任务会等待上一个任务执行完后,再开始执行 */ jobDetail.setConcurrent(false); jobDetail.setName("marketing_activity");// 设置任务的名字 jobDetail.setGroup("marketing_activity");// 设置任务的分组,这些属性都可以存储在数据库中,在多任务的时候使用 /* * 为需要执行的实体类对应的对象 */ jobDetail.setTargetObject(task); /* * marketingActivity为需要执行的方法 * 通过这几个配置,告诉JobDetailFactoryBean我们需要执行定时执行ScheduleTask类中的marketingActivity方法 */ jobDetail.setTargetMethod("marketingActivity"); return jobDetail; } /** * Details:配置定时任务的触发器,也就是什么时候触发执行定时任务 */ @Bean(name = "marketingActivityJobTrigger") public CronTriggerFactoryBean cronJobTrigger(MethodInvokingJobDetailFactoryBean jobDetail) { CronTriggerFactoryBean tigger = new CronTriggerFactoryBean(); tigger.setJobDetail(jobDetail.getObject()); tigger.setCronExpression("0 0 1 * * ?");// 初始时的cron表达式 ,每天1点执行 tigger.setName("marketing_activity");// trigger的name return tigger; } /** * Details:定义quartz调度工厂 */ @Bean(name = "marketingActivityScheduler") public SchedulerFactoryBean schedulerFactory(Trigger cronJobTrigger) { SchedulerFactoryBean bean = new SchedulerFactoryBean(); // 用于quartz集群,QuartzScheduler 启动时更新己存在的Job bean.setOverwriteExistingJobs(true); // 延时启动,应用启动1秒后 bean.setStartupDelay(1); // 注册触发器 bean.setTriggers(cronJobTrigger); return bean; } }
3、定时查库,并更新任务
package cloud.app.prod.home.quartz.mem; import javax.annotation.Resource; import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import cloud.app.prod.home.common.FailException; import cloud.app.prod.home.mem.vo.MarketingActivitiesVO; import cloud.app.prod.home.utils.DSHUtils; /** * Author : YongBo Xie </br> * File Name: ScheduleRefreshDatabase.java </br> * Created Date: 2018年3月31日 下午3:58:08 </br> * Modified Date: 2018年3月31日 下午3:58:08 </br> * Version: 1.0 </br> */ @Configuration @EnableScheduling @Component public class ScheduleRefreshCron { @Resource(name = "marketingActivityJobDetail") private JobDetail jobDetail; @Resource(name = "marketingActivityJobTrigger") private CronTrigger cronTrigger; @Resource(name = "marketingActivityScheduler") private Scheduler scheduler; @Scheduled(fixedRate = 5000) // 每隔5s查库,并根据查询结果决定是否重新设置定时任务 // @Scheduled(cron = "0 0 1 * * ?") public void scheduleUpdateCronTrigger() throws SchedulerException, FailException { CronTrigger trigger = (CronTrigger) scheduler.getTrigger(cronTrigger.getKey()); String currentCron = trigger.getCronExpression();// 当前Trigger使用的 String searchCron = "0 41 10 ? * *";// 从数据库查询出来的 System.out.println(currentCron); System.out.println(searchCron); // 如果当前使用的cron表达式和从数据库中查询出来的cron表达式一致,则不刷新任务 if (!currentCron.equals(searchCron)) { MarketingActivitiesVO marketingActivitiesVO = new MarketingActivitiesVO(); marketingActivitiesVO.setId(DSHUtils.generateUUID()); // 表达式调度构建器 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(searchCron); // 按新的cronExpression表达式重新构建trigger trigger = (CronTrigger) scheduler.getTrigger(cronTrigger.getKey()); trigger = trigger.getTriggerBuilder().withIdentity(cronTrigger.getKey()) .withSchedule(scheduleBuilder).build(); // 按新的trigger重新设置job执行 scheduler.rescheduleJob(cronTrigger.getKey(), trigger); currentCron = searchCron; } } }
二、可传参任务
在Spring Boot中使用Quartz时,如果job涉及到调用其他自定义方法,需要通过定义Job Factory实现自动注入
传参关键代码:
1)、接收参数
@Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { JobDataMap dataMap = context.getJobDetail().getJobDataMap(); MarketingActivitiesVO marketingActivitiesVO = (MarketingActivitiesVO) dataMap.get("marketingActivitiesVO"); }
2)、传入参数
// 创建一项作业 JobDetail job = JobBuilder.newJob(ScheduleTask.class) .withIdentity("marketingActivityJob", "marketingActivityGroup") .build(); // 设置参数 MarketingActivitiesVO marketingActivitiesVO = new MarketingActivitiesVO(); marketingActivitiesVO.setId(DSHUtils.generateUUID()); job.getJobDataMap().put("marketingActivitiesVO", marketingActivitiesVO);
1、自定义MyJobFactory类
package cloud.app.prod.home.quartz; import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.scheduling.quartz.AdaptableJobFactory; import org.springframework.stereotype.Component; /** * Author : YongBo Xie </br> * File Name: MyJobFactory.java </br> * Created Date: 2018年4月2日 下午3:27:30 </br> * Modified Date: 2018年4月2日 下午3:27:30 </br> * Version: 1.0 </br> */ @Component public class MyJobFactory extends AdaptableJobFactory { @Autowired private AutowireCapableBeanFactory capableBeanFactory; @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { // 调用父类的方法 Object jobInstance = super.createJobInstance(bundle); // 进行注入 capableBeanFactory.autowireBean(jobInstance); return jobInstance; } }
2、配置SchedulerFactoryBean
package cloud.app.prod.home.quartz; import java.io.IOException; import java.util.Properties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import cloud.app.prod.home.quartz.MyJobFactory; /** * Author : YongBo Xie </br> * File Name: QuartzConfigration.java </br> * Created Date: 2018年3月31日 下午3:42:04 </br> * Modified Date: 2018年3月31日 下午3:42:04 </br> * Version: 1.0 </br> */ @Configuration @EnableScheduling public class QuartzConfigration { @Autowired private MyJobFactory myJobFactory; @Bean public SchedulerFactoryBean schedulerFactoryBean() throws IOException { SchedulerFactoryBean factory = new SchedulerFactoryBean(); // 用于quartz集群,QuartzScheduler 启动时更新己存在的Job factory.setOverwriteExistingJobs(true); // 延时启动,应用启动1秒后 // factory.setStartupDelay(1); // 加载quartz数据源配置 // factory.setQuartzProperties(quartzProperties()); // 自定义Job Factory,用于Spring注入 factory.setJobFactory(myJobFactory); return factory; } /** * 加载quartz数据源配置 * * @return * @throws IOException */ @Bean public Properties quartzProperties() throws IOException { PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties")); propertiesFactoryBean.afterPropertiesSet(); return propertiesFactoryBean.getObject(); } }
3、执行任务类
package cloud.app.prod.home.quartz.mem; import org.apache.log4j.Logger; import org.quartz.DisallowConcurrentExecution; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.quartz.QuartzJobBean; import org.springframework.stereotype.Component; import cloud.app.prod.home.common.FailException; import cloud.app.prod.home.mem.vo.MarketingActivitiesVO; import cloud.app.prod.home.rabbitmq.mem.MarketingActivitieRabbitMqSender; /** * Author : YongBo Xie </br> * File Name: ScheduleTask.java </br> * Created Date: 2018年3月31日 下午3:37:43 </br> * Modified Date: 2018年3月31日 下午3:37:43 </br> * Version: 1.0 </br> */ @Configuration @Component // 此注解必加 @EnableScheduling // 此注解必加 @DisallowConcurrentExecution // 任务同步 public class ScheduleTask extends QuartzJobBean { private static Logger logger = Logger.getLogger(ScheduleTask.class); @Autowired private MarketingActivitieRabbitMqSender marketingActivitieRabbitMqSender; @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { logger.info("execute activity"); JobDataMap dataMap = context.getJobDetail().getJobDataMap(); MarketingActivitiesVO marketingActivitiesVO = (MarketingActivitiesVO) dataMap.get("marketingActivitiesVO"); logger.info("marketingActivitiesVO.id: " + marketingActivitiesVO.getId()); try { logger.info("marketingActivitieRabbitMqSender: " + marketingActivitieRabbitMqSender); marketingActivitieRabbitMqSender.sendRabbitmqDirect(marketingActivitiesVO); } catch (FailException e) { e.printStackTrace(); } } }
4、设置任务类
package cloud.app.prod.home.quartz.mem; import java.util.Date; import org.apache.log4j.Logger; import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.DateBuilder; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.TriggerBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.stereotype.Component; import cloud.app.prod.home.common.FailException; import cloud.app.prod.home.mem.vo.MarketingActivitiesVO; import cloud.app.prod.home.utils.DSHUtils; /** * Author : YongBo Xie </br> * Project Name: DSH-SCRM </br> * Created Date: 2018年3月31日 下午3:58:08 </br> * Modified Date: 2018年3月31日 下午3:58:08 </br> * Version: 1.0 </br> */ @Configuration @EnableScheduling @Component public class ScheduleRefreshCron { private static Logger logger = Logger.getLogger(ScheduleRefreshCron.class); @Autowired private SchedulerFactoryBean schedulerFactoryBean; @Scheduled(fixedRate = 5000) // 每隔5s查库 // @Scheduled(cron = "0 0 1 * * ?") public void scheduleUpdateCronTrigger() throws FailException, SchedulerException { try{ logger.info("----- scheduling job --------"); String searchCron = "*/5 * * * * ?";// 从数据库查询出来的 // 获取一个调度器 // SchedulerFactory factory = new StdSchedulerFactory(); // Scheduler scheduler = factory.getScheduler(); Scheduler scheduler = schedulerFactoryBean.getScheduler(); // 创建一项作业 JobDetail job = JobBuilder.newJob(ScheduleTask.class) .withIdentity("marketingActivityJob", "marketingActivityGroup") .build(); // 设置参数 MarketingActivitiesVO marketingActivitiesVO = new MarketingActivitiesVO(); marketingActivitiesVO.setId(DSHUtils.generateUUID()); job.getJobDataMap().put("marketingActivitiesVO", marketingActivitiesVO); // 作业的执行时间(当前时间的下一分钟) Date runTime = DateBuilder.evenMinuteDate(new Date()); // 创建一个触发器 CronTrigger trigger = (CronTrigger)TriggerBuilder.newTrigger() .withIdentity("marketingActivityTrigger", "marketingActivityGroup") .startAt(runTime) //该触发器将在runTime时执行作业 .endAt(new Date(System.currentTimeMillis()+5*1000+60*1000)) //该触发器将在runTime时结束作业 .withSchedule(CronScheduleBuilder.cronSchedule(searchCron)) // 具体执行时间 .build(); // 告诉调度器使用该触发器来安排作业 scheduler.scheduleJob(job, trigger); // 启动调度器 scheduler.start(); logger.info("------ started scheduler -------"); logger.info("------ waiting 2 minutes ------"); Thread.sleep(2*60*1000); logger.info("------- shutting down ------"); // 关闭调度器 scheduler.shutdown(true); logger.info("------- shutdown complete -------"); }catch(Exception e){ logger.error(DSHUtils.formatException(e)); throw new FailException(e.getMessage()); } } }