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


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


目录
相关文章
|
3月前
|
Java Spring
一键注入 Spring 成员变量,顺序编程
介绍了一款针对Spring框架开发的插件,旨在解决开发中频繁滚动查找成员变量注入位置的问题。通过一键操作(如Ctrl+1),该插件可自动在类顶部添加`@Autowired`注解及其成员变量声明,同时保持光标位置不变,有效提升开发效率和代码编写流畅度。适用于IntelliJ IDEA 2023及以上版本。
一键注入 Spring 成员变量,顺序编程
|
5月前
|
Java Spring
在使用Spring的`@Value`注解注入属性值时,有一些特殊字符需要注意
【10月更文挑战第9天】在使用Spring的`@Value`注解注入属性值时,需注意一些特殊字符的正确处理方法,包括空格、引号、反斜杠、新行、制表符、逗号、大括号、$、百分号及其他特殊字符。通过适当包裹或转义,确保这些字符能被正确解析和注入。
325 3
|
5月前
|
Java 测试技术 程序员
为什么Spring不推荐@Autowired用于字段注入?
作为Java程序员,Spring框架在日常开发中使用频繁,其依赖注入机制带来了极大的便利。然而,尽管@Autowired注解简化了依赖注入,Spring官方却不推荐在字段上使用它。本文将探讨字段注入的现状及其存在的问题,如难以进行单元测试、违反单一职责原则及易引发NPE等,并介绍为何Spring推荐构造器注入,包括增强代码可读性和维护性、方便单元测试以及避免NPE等问题。通过示例代码展示如何将字段注入重构为构造器注入,提高代码质量。
146 1
|
5月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
148 1
|
7月前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
78 0
|
7月前
|
安全 Java 开发者
开发者必看!@Resource与private final的较量,Spring Boot注入技巧大揭秘,你不可不知的细节!
【8月更文挑战第29天】Spring Boot作为热门Java框架,其依赖注入机制备受关注。本文通过对比@Resource(JSR-250规范)和@Autowired(Spring特有),并结合private final声明的字段注入,详细探讨了两者的区别与应用场景。通过示例代码展示了@Resource按名称注入及@Autowired按类型注入的特点,并分析了它们在注入时机、依赖性、线程安全性和单一职责原则方面的差异,帮助开发者根据具体需求选择最合适的注入策略。
265 0
|
7月前
|
XML Java 数据格式
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
这篇文章是Spring5框架的实战教程,主题是IOC容器中Bean的集合属性注入,通过XML配置方式。文章详细讲解了如何在Spring中注入数组、List、Map和Set类型的集合属性,并提供了相应的XML配置示例和Java类定义。此外,还介绍了如何在集合中注入对象类型值,以及如何使用Spring的util命名空间来实现集合的复用。最后,通过测试代码和结果展示了注入效果。
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
|
7月前
|
Java 关系型数据库 MySQL
SpringBoot 集成 Quartz + MySQL
SpringBoot 集成 Quartz + MySQL
159 1
|
8月前
|
Java Spring
spring注入的几种方式
spring注入的几种方式
51 0
|
9月前
|
Java Spring 容器
spring如何进行依赖注入,通过set方法把Dao注入到serves
spring如何进行依赖注入,通过set方法把Dao注入到serves

热门文章

最新文章