欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

quartz框架定时任务整合springboot报空指针异常解决

程序员文章站 2022-03-26 20:33:47
首先我们谈一下为什么汇报空指针异常呢??报错如下: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) [...

首先我们谈一下为什么汇报空指针异常呢??

报错如下:

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表达式及其含义:

quartz框架定时任务整合springboot报空指针异常解决
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

相关标签: quartz springboot