quartz(四)如何在job中使用spring自动注入

简介: quartz(四)如何在job中使用spring自动注入

跨年篇:祝愿大家万事随心,吃饭吃得饱,睡觉睡得好,没有烦扰。


一、背景



我在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


  1. 为啥会有空指针?


答:出现这个原因是没有注入成功,所以为null,再通过这个对象调用mapper的方法不报错才怪。


  1. 这里你会很奇怪吧,明明使用了@Resource和@Autowired来注入,为什么注入后的对象还是null呢?


答:这里我来解释一下,这个是因为Job的实现类并没有加入到spring容器中,自然无法使用自动注入。


  1. 那进入今天的主题吧,怎么在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中


  1. 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;
  }
}


  1. 通过配置类创建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();
    }
}


  1. 通过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);
    }
}


三、测试结果



  1. swagger调用

640.png

  1. 执行日志
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


从日志结果来看,任务执行正常,没有出现空指针异常了。希望采纳!


目录
相关文章
|
2月前
|
XML Java 开发者
Spring Boot中的bean注入方式和原理
Spring Boot中的bean注入方式和原理
82 0
|
4月前
|
Java 程序员 Spring
Spring5深入浅出篇:Spring对象属性注入详解
Spring5深入浅出篇:Spring对象属性注入详解
|
5月前
|
Java 开发者 Spring
Spring中获取Bean对象的三种注入方式和两种注入方法
Spring中获取Bean对象的三种注入方式和两种注入方法
|
6月前
|
Java 测试技术 开发者
spring入门基本介绍及注入方式---详细介绍
spring入门基本介绍及注入方式---详细介绍
61 0
|
7天前
|
Java Spring 容器
Spring注入
Spring注入
25 13
|
19天前
|
JSON Java 数据库连接
属性注入掌握:Spring Boot配置属性的高级技巧与最佳实践
属性注入掌握:Spring Boot配置属性的高级技巧与最佳实践
28 1
|
2月前
|
XML Java 数据格式
解释Spring中一个bean的注入过程
解释Spring中一个bean的注入过程
|
3月前
|
Java Spring
Spring 使用注解注入失败,调用内容时提示空指针
Spring 使用注解注入失败,调用内容时提示空指针
15 0
|
3月前
|
Java Spring
Spring注入类的两种形式
Spring注入类的两种形式
49 0
Spring注入类的两种形式
|
4月前
|
XML 缓存 Java
Spring5源码(7)-lookup-method和replace-method注入
Spring5源码(7)-lookup-method和replace-method注入
21 0