quartz框架定时任务整合springboot报空指针异常解决
首先我们谈一下为什么汇报空指针异常呢??
报错如下:
org.quartz.SchedulerException: Job threw an unhandled exception.
at org.quartz.core.JobRunShell.run(JobRunShell.java:213) ~[quartz-2.3.2.jar:na]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [quartz-2.3.2.jar:na]
Caused by: java.lang.NullPointerException: null
at com.union.quartz.MyJob.execute(MyJob.java:37) ~[classes/:na]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.3.2.jar:na]
... 1 common frames omitted
**报这个错误的原因是quartz这个框架的job任务类是由java反射得到的 , 他是不交由spring管理的
quartz的执行在@Autowired之前执行的
**
那么我们该如何解决呢?
我们可以使用springbean获取对象
我们写一个springUtil工具类
package com.union.utils; import org.springframework.aop.framework.AopContext; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Component; /**
* spring工具类 方便在非spring管理环境中获取bean
*
* @author admin
*/ @Component public final class SpringUtils implements BeanFactoryPostProcessor { /** Spring应用上下文环境 */ private static ConfigurableListableBeanFactory beanFactory; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { SpringUtils.beanFactory = beanFactory; } /**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws org.springframework.beans.BeansException
*
*/ @SuppressWarnings("unchecked") public static <T> T getBean(String name) throws BeansException { return (T) beanFactory.getBean(name); } /**
* 获取类型为requiredType的对象
*
* @param clz
* @return
* @throws org.springframework.beans.BeansException
*
*/ public static <T> T getBean(Class<T> clz) throws BeansException { T result = (T) beanFactory.getBean(clz); return result; } /**
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
*
* @param name
* @return boolean
*/ public static boolean containsBean(String name) { return beanFactory.containsBean(name); } /**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
*
* @param name
* @return boolean
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/ public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { return beanFactory.isSingleton(name); } /**
* @param name
* @return Class 注册对象的类型
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/ public static Class<?> getType(String name) throws NoSuchBeanDefinitionException { return beanFactory.getType(name); } /**
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
*
* @param name
* @return
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/ public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { return beanFactory.getAliases(name); } /**
* 获取aop代理对象
*
* @param invoker
* @return
*/ @SuppressWarnings("unchecked") public static <T> T getAopProxy(T invoker) { return (T) AopContext.currentProxy(); } }
在job类中手动获取bean注入service
UserService userService = SpringUtils.getBean(UserService.class); TokenService tokenService = SpringUtils.getBean(TokenService.class);
如下写的是一个定时生日提醒的功能实现
package com.union.quartz;//MyJob.java 任务类 import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import net.sf.json.JSONObject; import com.sun.el.parser.ParseException; import com.union.controller.UserController; import com.union.pojo.User; import com.union.service.TokenService; import com.union.service.UserService; import com.union.utils.SpringUtils; import com.union.utils.Utils; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.annotation.PostConstruct; @Component public class MyJob implements Job { UserService userService = SpringUtils.getBean(UserService.class); TokenService tokenService = SpringUtils.getBean(TokenService.class); public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println(new Date() + ": doing something..."); List<User> users = userService.selUserByBirth(Utils.strToDate1(Utils.nowDate())); System.out.println(users.toString()); for (int i = 0; i < users.size(); i++) { JSONObject send = new JSONObject(); JSONObject send1 = new JSONObject(); JSONObject name = new JSONObject(); JSONObject birth = new JSONObject(); send1.put("first", name); name.put("value", users.get(i).getName()); name.put("color", "#000000"); send1.put("keyword1", birth); birth.put("value", Utils.dateToStr(users.get(i).getBirth())); birth.put("color", "#000000"); send.put("touser", users.get(i).getOpenid()); send.put("template_id", "IkNYNz7-Z7bx6NGvykFbe6DD_eiKuMM2HIAAGeXOsqo"); send.put("data", send1); String access_token = tokenService.selToken().getAccess_token(); String send2 = null; try { send2 = Utils.send( "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + access_token, send, "utf-8"); } catch (ParseException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } catch (IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } System.out.println(send2); } } }
定时提醒的管理类如下
通过调用此类的方法可实现对job任务的初始化 , 修改触发时间 , 结束定时任务这些功能
package com.union.quartz; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; public class QuartzManager { private static SchedulerFactory schedulerFactory = new StdSchedulerFactory(); /**
* @Description: 添加一个定时任务
*
* @param jobName 任务名
* @param jobGroupName 任务组名
* @param triggerName 触发器名
* @param triggerGroupName 触发器组名
* @param jobClass 任务
* @param cron 时间设置,参考quartz说明文档
*/ @SuppressWarnings({ "unchecked", "rawtypes" }) public static void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron) { try { Scheduler sched = schedulerFactory.getScheduler(); // 任务名,任务组,任务执行类 JobDetail jobDetail= JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build(); // 触发器 TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger(); // 触发器名,触发器组 triggerBuilder.withIdentity(triggerName, triggerGroupName); triggerBuilder.startNow(); // 触发器时间设定 triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron)); // 创建Trigger对象 CronTrigger trigger = (CronTrigger) triggerBuilder.build(); // 调度容器设置JobDetail和Trigger sched.scheduleJob(jobDetail, trigger); // 启动 if (!sched.isShutdown()) { sched.start(); } } catch (Exception e) { throw new RuntimeException(e); } } /**
* @Description: 修改一个任务的触发时间
*
* @param jobName
* @param jobGroupName
* @param triggerName 触发器名
* @param triggerGroupName 触发器组名
* @param cron 时间设置,参考quartz说明文档
*/ public static void modifyJobTime(String jobName, String jobGroupName, String triggerName, String triggerGroupName, String cron) { try { Scheduler sched = schedulerFactory.getScheduler(); TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName); CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey); if (trigger == null) { return; } String oldTime = trigger.getCronExpression(); if (!oldTime.equalsIgnoreCase(cron)) { /** 方式一 :调用 rescheduleJob 开始 */ // 触发器 TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger(); // 触发器名,触发器组 triggerBuilder.withIdentity(triggerName, triggerGroupName); triggerBuilder.startNow(); // 触发器时间设定 triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron)); // 创建Trigger对象 trigger = (CronTrigger) triggerBuilder.build(); // 方式一 :修改一个任务的触发时间 sched.rescheduleJob(triggerKey, trigger); /** 方式一 :调用 rescheduleJob 结束 */ /** 方式二:先删除,然后在创建一个新的Job */ //JobDetail jobDetail = sched.getJobDetail(JobKey.jobKey(jobName, jobGroupName)); //Class<? extends Job> jobClass = jobDetail.getJobClass(); //removeJob(jobName, jobGroupName, triggerName, triggerGroupName); //addJob(jobName, jobGroupName, triggerName, triggerGroupName, jobClass, cron); /** 方式二 :先删除,然后在创建一个新的Job */ } } catch (Exception e) { throw new RuntimeException(e); } } /**
* @Description: 移除一个任务
*
* @param jobName
* @param jobGroupName
* @param triggerName
* @param triggerGroupName
*/ public static void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) { try { Scheduler sched = schedulerFactory.getScheduler(); TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName); sched.pauseTrigger(triggerKey);// 停止触发器 sched.unscheduleJob(triggerKey);// 移除触发器 sched.deleteJob(JobKey.jobKey(jobName, jobGroupName));// 删除任务 } catch (Exception e) { throw new RuntimeException(e); } } /**
* @Description:启动所有定时任务
*/ public static void startJobs() { try { Scheduler sched = schedulerFactory.getScheduler(); sched.start(); } catch (Exception e) { throw new RuntimeException(e); } } /**
* @Description:关闭所有定时任务
*/ public static void shutdownJobs() { try { Scheduler sched = schedulerFactory.getScheduler(); if (!sched.isShutdown()) { sched.shutdown(); } } catch (Exception e) { throw new RuntimeException(e); } } }
通过springboot自身的定时任务@Scheduled(cron = "0 58 16 * * ?")
定时修改job任务的触发时间
package com.union.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.union.pojo.Time; import com.union.quartz.QuartzManager; import com.union.service.TimeService; /**
* <p>
* 前端控制器
* </p>
*
* @author 姚云峰
* @since 2020-08-08
*/ @RestController @RequestMapping("/time/time") public class TimeController { @Autowired private TimeService timeService; public static String JOB_NAME = "动态任务调度"; public static String TRIGGER_NAME = "动态任务触发器"; public static String JOB_GROUP_NAME = "XLXXCC_JOB_GROUP"; public static String TRIGGER_GROUP_NAME = "XLXXCC_JOB_GROUP"; @Scheduled(cron = "0 58 16 * * ?") public void getTime() { Time one = timeService.getOne(null); String s = one.getTime().toString(); String[] str = s.split(" "); s = str[3]; str = s.split(":"); s = ""; for (int i = str.length - 1; i >= 0; i--) { s = s + str[i] + " "; } s = s + "* * ?"; System.out.println(s); try { // QuartzManager.removeJob(JOB_NAME, JOB_GROUP_NAME, TRIGGER_NAME, TRIGGER_GROUP_NAME); QuartzManager.modifyJobTime(JOB_NAME, JOB_GROUP_NAME, TRIGGER_NAME, TRIGGER_GROUP_NAME, s); } catch (Exception e) { e.printStackTrace(); } } }
为了能让springboot在完全启动后再执行定时任务初始化 触发job任务的功能实现 , 我们引入了如下类
package com.union.lifecycle; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import com.union.controller.TimeController; import com.union.quartz.MyJob; import com.union.quartz.QuartzManager; /**
* @author lnj
* createTime 2018-11-07 22:37
**/ @Component public class ApplicationRunnerImpl implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { QuartzManager.addJob(TimeController.JOB_NAME, TimeController.JOB_GROUP_NAME, TimeController.TRIGGER_NAME, TimeController.TRIGGER_GROUP_NAME, MyJob.class, "* * * * * ?"); System.out.println("通过实现ApplicationRunner接口,在spring boot项目启动后打印参数"); } }
在此类中写的QuartzManager.addJob(TimeController.JOB_NAME, TimeController.JOB_GROUP_NAME, TimeController.TRIGGER_NAME, TimeController.TRIGGER_GROUP_NAME, MyJob.class, "* * * * * ?");
调用添加定时任务 , 使定时任务初始化完成 , 即触发job任务类
补充知识:
此这是引用这篇文章
定时任务的cron表达式及其含义:
Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式:
(1) Seconds Minutes Hours DayofMonth Month DayofWeek Year
(2)Seconds Minutes Hours DayofMonth Month DayofWeek
0 0 2 1 * ? * 表示在每月的1日的凌晨2点调整任务
(2)0 15 10 ? * MON-FRI 表示周一到周五每天上午10:15执行作业
(3)0 15 10 ? 6L 2002-2006 表示2002-2006年的每个月的最后一个星期五上午10:15执行作
(4)0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
(5)0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
(6)0 0 12 ? * WED 表示每个星期三中午12点
(7)0 0 12 * * ? 每天中午12点触发
(8)0 15 10 ? * * 每天上午10:15触发
(9)0 15 10 * * ? 每天上午10:15触发
(10)0 15 10 * * ? * 每天上午10:15触发
(11)0 15 10 * * ? 2005 2005年的每天上午10:15触发
(12)0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发
(13)0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
(14)0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
(15)0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
(16)0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
(17)0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
(18)0 15 10 15 * ? 每月15日上午10:15触发
(19)0 15 10 L * ? 每月最后一日的上午10:15触发
(20)0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
(21)0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发
(22)0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
本文地址:https://blog.csdn.net/weixin_44735933/article/details/107885327
下一篇: Edius怎么给视频添加剪切点?