跨年篇:祝愿大家万事随心,吃饭吃得饱,睡觉睡得好,没有烦扰。
一、背景
我在Job实现类中使用@Resource和@Autowired注入mapper类时,出现了如下的错误:
2020-12-31 20:41:07.311 ERROR 22456 --- [eduler_Worker-5] org.quartz.core.ErrorLogger : Job (repeat.test2 threw an exception. org.quartz.SchedulerException: Job threw an unhandled exception. at org.quartz.core.JobRunShell.run(JobRunShell.java:213) at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) Caused by: java.lang.NullPointerException: null at com.example.docker_images.job.HelloJob.execute(HelloJob.java:26) at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ... 1 common frames omitted
- 为啥会有空指针?
答:出现这个原因是没有注入成功,所以为null,再通过这个对象调用mapper的方法不报错才怪。
- 这里你会很奇怪吧,明明使用了@Resource和@Autowired来注入,为什么注入后的对象还是null呢?
答:这里我来解释一下,这个是因为Job的实现类并没有加入到spring容器中,自然无法使用自动注入。
- 那进入今天的主题吧,怎么在Job实现类中使用spring的自动注入呢?
答:自然是将Job加入到Spring容器中,然后就可以执行自动注入了。事先申明一下,这篇文章是在quartz(三)任务持久化-jdbc篇(一看就会)上优化的,有兴趣的可以看一下三。注入方式如下:
二、代码实现
通用代码(在三的基础上加了数据库的操作):
import com.example.docker_images.entity.User; import com.example.docker_images.entity.common.CwConstant; import com.example.docker_images.service.IUserService; import lombok.extern.slf4j.Slf4j; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.Resource; import java.util.List; @Slf4j public class HelloJob implements Job { @Resource private IUserService iUserService; @Override public void execute(JobExecutionContext context) throws JobExecutionException { //通过jobDetail获取数据 String payload = context.getTrigger().getJobDataMap().getString(CwConstant.PAYLOAD); System.out.println(payload); System.out.println("hello world!!!"); List<User> list = iUserService.list(); System.out.println(list.size()); } }
1. pom配置
<!-- Quartz坐标 --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.1</version> </dependency> <!--springboot-quartz--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
2. 通过配置类将spring上下文注入到JobFactory中
- spring容器操作类
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 ApplicationContextHolder extends SpringBeanJobFactory implements ApplicationContextAware { private static ApplicationContext context; private transient AutowireCapableBeanFactory beanFactory; @Override public void setApplicationContext(final ApplicationContext context) { beanFactory = context.getAutowireCapableBeanFactory(); ApplicationContextHolder.context = context; } @Override protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { final Object job = super.createJobInstance(bundle); beanFactory.autowireBean(job); return job; } public static ApplicationContext getContext() { return context; } }
- 通过配置类创建schedulerFactory
import com.example.docker_images.job.ApplicationContextHolder; import org.quartz.Scheduler; import org.quartz.spi.JobFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import java.io.IOException; @Configuration public class QuartzConfiguration { @Autowired private ApplicationContext applicationContext; /** * Create the job factory bean * * @return Job factory bean */ @Bean public JobFactory jobFactory() { ApplicationContextHolder jobFactory = new ApplicationContextHolder(); jobFactory.setApplicationContext(applicationContext); return jobFactory; } /** * Create the Scheduler Factory bean * * @return scheduler factory object */ @Bean public SchedulerFactoryBean schedulerFactory() { SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setAutoStartup(true); factory.setSchedulerName("CarWash Scheduler"); factory.setOverwriteExistingJobs(true); factory.setJobFactory(jobFactory()); return factory; } /** * 获得Scheduler 对象 * * @return * @throws IOException */ @Bean public Scheduler scheduler() throws IOException { return schedulerFactory().getScheduler(); } }
- 通过schedulerFactory获取Scheduler
import com.example.docker_images.entity.common.APIResult; import com.example.docker_images.entity.common.CwConstant; import com.example.docker_images.entity.common.ReturnCodeEnum; import com.example.docker_images.entity.job.JobPayload; import com.example.docker_images.job.HelloJob; import com.example.docker_images.service.JobService; import com.example.docker_images.util.DateUtil; import lombok.extern.slf4j.Slf4j; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import org.quartz.impl.matchers.GroupMatcher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import javax.annotation.Resource; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; import static org.quartz.CronScheduleBuilder.cronSchedule; import static org.quartz.TriggerBuilder.newTrigger; @Slf4j @Service public class JobServiceImpl implements JobService { //这里不可以使用@Resource来注入,必须使用@Autowired @Autowired private SchedulerFactoryBean schedulerFactory; @Override public APIResult initiate(JobPayload payload) { try { Scheduler scheduler = schedulerFactory.getScheduler(); JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity(payload.getName(), payload.getGroup()).build(); switch (payload.getScheduleType()){ case CwConstant.JobType.NOW: Trigger build = newTrigger().withIdentity(payload.getName(), payload.getGroup()) .startNow().usingJobData(CwConstant.PAYLOAD,payload.getDataMap()).withDescription(payload.getDesc()).build(); scheduler.scheduleJob(jobDetail,build); scheduler.start(); log.info("成功创建即刻执行任务!!!"); break; case CwConstant.JobType.ONCE: Trigger trigger = newTrigger().withIdentity(payload.getName(), payload.getGroup()) .startAt(DateUtil.parseYYYYMMDD(payload.getCron())) .usingJobData(CwConstant.PAYLOAD,payload.getDataMap()) .withDescription(payload.getDesc()) .build(); scheduler.scheduleJob(jobDetail,trigger); scheduler.start(); log.info("成功创建未来执行一次任务!!!"); break; case CwConstant.JobType.REPEAT: CronTrigger cronTrigger = newTrigger().withIdentity(payload.getName(), payload.getGroup()) .usingJobData(CwConstant.PAYLOAD,payload.getDataMap()).withDescription(payload.getDesc()) .withSchedule(cronSchedule(payload.getCron())).build(); scheduler.scheduleJob(jobDetail,cronTrigger); scheduler.start(); log.info("成功创建重复任务!!!"); break; } } catch (Exception e) { log.info("job initial failure:{}",e); return APIResult.error(ReturnCodeEnum.JOB_INITIAL_FAILURE); } return APIResult.success(null); } @Override public APIResult getJobList(String name, String groupName) { List<JobPayload> jobs = new ArrayList<>(); try { Scheduler scheduler = schedulerFactory.getScheduler(); Set<JobKey> jobKeys; if(StringUtils.isEmpty(groupName)){ if(!StringUtils.isEmpty(name)){ CronTrigger trigger = (CronTrigger)scheduler.getTrigger(new TriggerKey(name, groupName)); jobs.add(JobPayload.getInstance(trigger)); return APIResult.success(jobs); } jobKeys = scheduler.getJobKeys(GroupMatcher.anyGroup()); }else { jobKeys = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName)); } jobKeys.forEach(jobKey -> { try { CronTrigger trigger = (CronTrigger)scheduler.getTrigger(new TriggerKey(jobKey.getName(), jobKey.getGroup())); JobPayload instance = JobPayload.getInstance(trigger); jobs.add(instance); } catch (SchedulerException e) { log.info("任务不存在:{}",e); } }); log.info("获取任务列表成功!!!"); }catch (Exception e){ log.info("任务列表获取失败{}",e); return APIResult.success(jobs); } return APIResult.success(jobs); } @Override public APIResult update(JobPayload jobPayload) { try { Scheduler scheduler = schedulerFactory.getScheduler(); TriggerKey triggerKey = new TriggerKey(jobPayload.getName(), jobPayload.getGroup()); switch (jobPayload.getScheduleType()){ case CwConstant.JobType.ONCE: //获取trigger,修改执行频率 Trigger trigger = scheduler.getTrigger(triggerKey); Date startTime = trigger.getStartTime(); Date date = DateUtil.parseYYYYMMDD(jobPayload.getCron()); boolean equals = date.equals(startTime); if(!equals){ Trigger build = newTrigger().withIdentity(triggerKey).startAt(date) .withDescription(jobPayload.getDesc()) .usingJobData(CwConstant.PAYLOAD,jobPayload.getDataMap()) .build(); scheduler.rescheduleJob(triggerKey,build); } case CwConstant.JobType.REPEAT: CronTrigger cronTrigger = (CronTrigger)scheduler.getTrigger(triggerKey); String cronExpression = cronTrigger.getCronExpression(); if(!ObjectUtils.nullSafeEquals(jobPayload.getCron(),cronExpression)){ CronTrigger build = newTrigger().withIdentity(triggerKey) .withDescription(jobPayload.getDesc()) .usingJobData(CwConstant.PAYLOAD,jobPayload.getDataMap()) .withSchedule(cronSchedule(jobPayload.getCron())) .build(); scheduler.rescheduleJob(triggerKey,build); } } log.info("更新任务成功!!!"); }catch (Exception e){ log.info("修改失败:{}",e); return APIResult.error(ReturnCodeEnum.JOB_INITIAL_FAILURE); } return APIResult.success(null); } @Override public APIResult delete(String name, String groupName) { try { Scheduler scheduler = schedulerFactory.getScheduler(); TriggerKey triggerKey = new TriggerKey(name, groupName); if(scheduler.checkExists(triggerKey)){ scheduler.pauseTrigger(triggerKey); scheduler.unscheduleJob(triggerKey); scheduler.deleteJob(new JobKey(name,groupName)); log.info("任务删除成功!!!"); } }catch (Exception e){ log.info("删除任务失败:{}",e); return APIResult.error(ReturnCodeEnum.JOB_INITIAL_FAILURE); } return APIResult.success(null); } @Override public APIResult pauseJob(String name, String groupName) { try { Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.pauseTrigger(new TriggerKey(name, groupName)); log.info("暂停任务成功!!!"); }catch (Exception e){ log.info("暂停任务失败:{}",e); return APIResult.error(ReturnCodeEnum.JOB_INITIAL_FAILURE); } return APIResult.success(null); } @Override public APIResult resumeJob(String name, String groupName) { try { Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.resumeTrigger(new TriggerKey(name, groupName)); log.info("恢复任务成功!!!"); }catch (Exception e){ log.info("恢复任务失败:{}",e); return APIResult.error(ReturnCodeEnum.JOB_INITIAL_FAILURE); } return APIResult.success(null); } }
三、测试结果
- swagger调用
- 执行日志
2020-12-31 21:58:02.709 INFO 12472 --- [nio-8088-exec-2] c.e.d.service.impl.JobServiceImpl : 成功创建重复任务!!! {name:'ljl'} hello world!!! 2020-12-31 21:58:07.706 INFO 12472 --- [eduler_Worker-1] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited 2020-12-31 21:58:07.712 DEBUG 12472 --- [eduler_Worker-1] c.e.d.mapper.UserMapper.selectList : ==> Preparing: SELECT id,user_name FROM user 2020-12-31 21:58:07.720 DEBUG 12472 --- [eduler_Worker-1] c.e.d.mapper.UserMapper.selectList : ==> Parameters: 2020-12-31 21:58:07.754 DEBUG 12472 --- [eduler_Worker-1] c.e.d.mapper.UserMapper.selectList : <== Total: 2 2 {name:'ljl'} hello world!!! 2020-12-31 21:58:12.008 DEBUG 12472 --- [eduler_Worker-2] c.e.d.mapper.UserMapper.selectList : ==> Preparing: SELECT id,user_name FROM user 2020-12-31 21:58:12.008 DEBUG 12472 --- [eduler_Worker-2] c.e.d.mapper.UserMapper.selectList : ==> Parameters: 2020-12-31 21:58:12.036 DEBUG 12472 --- [eduler_Worker-2] c.e.d.mapper.UserMapper.selectList : <== Total: 2 2
从日志结果来看,任务执行正常,没有出现空指针异常了。希望采纳!