Springcloud学习笔记67--springboot 整合 任务调度框架Quartz

1.背景

定时任务Job的作业类中无法注入Service等由Spring容器所管理的Bean。例如下面这种情况,TaskCronJobService就无法成功注入。

import java.util.Iterator;
 
import javax.annotation.Resource;
 
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;
 
import com.pengjunlee.task.bean.TaskCronJob;
import com.pengjunlee.task.service.TaskCronJobService;
 
/**
 * 定时任务的作业类,需实现Job接口
 * */
@Component
public class MyJob implements Job {
 
    @Resource
    private TaskCronJobService taskCronJobService;
 
    @Override
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        System.out.println("执行定时任务:MyJob.execute()..."
                + System.currentTimeMillis());
        Iterable<TaskCronJob> findAll = taskCronJobService.findAll();
        Iterator<TaskCronJob> iterator = findAll.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next().getJobClassName());
        }
    }
 
}

  定时任务Job对象的实例化过程是在Quartz中进行的,而TaskCronJobService Bean是由Spring容器管理的,Quartz根本就察觉不到TaskCronJobService Bean的存在,故而无法将TaskCronJobService Bean装配到Job对象中。

  知道了问题出现的原因,解决的办法也就显而易见了:如果能够将Job Bean也纳入到Spring容器的管理之中的话,Spring容器自然能够为Job Bean自动装配好所需的依赖。

  通过查看Spring官方文档得知,Spring与Quartz集成使用的是SchedulerFactoryBean这个类,其所需引入Maven依赖如下。

        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.0</version>
        </dependency>
        <!-- 该依赖必加,里面有sping对schedule的支持 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>

以下是SchedulerFactoryBean类(spring-context-support 这个包)的部分关键代码。

package org.springframework.scheduling.quartz;
 
public class SchedulerFactoryBean extends SchedulerAccessor implements
        FactoryBean<Scheduler>, BeanNameAware, ApplicationContextAware,
        InitializingBean, DisposableBean, SmartLifecycle {
 
    @Override
    public void afterPropertiesSet() throws Exception {
        //---------------------------省略部分代码--------------------------
        // Get Scheduler instance from SchedulerFactory.
        try {
            this.scheduler = createScheduler(schedulerFactory, this.schedulerName);
            populateSchedulerContext();
 
            if (!this.jobFactorySet && !(this.scheduler instanceof RemoteScheduler)) {
                // Use AdaptableJobFactory as default for a local Scheduler, unless when
                // explicitly given a null value through the "jobFactory" bean property.
                this.jobFactory = new AdaptableJobFactory();
            }
            if (this.jobFactory != null) {
                if (this.jobFactory instanceof SchedulerContextAware) {
                    ((SchedulerContextAware) this.jobFactory).setSchedulerContext(this.scheduler.getContext());
                }
                this.scheduler.setJobFactory(this.jobFactory);
            }
        }
        //---------------------------省略部分代码--------------------------
    }
}

从以上代码可以看出:SchedulerFactoryBean 使用 AdaptableJobFactory 对Job对象进行实例化,如果未指定则SchedulerFactoryBean会自动创建一个,在这里如果我们能够将 SchedulerFactoryBean 的 jobFactory 指定为我们自定义的工厂实例的话,我们就能够有机会在Job实例化完成之后对其进行处理,并将其纳入到Spring容器的管理之中。

2. springboot整合quartz实践

例如下面这段代码,创建一个SchedulerFactoryBean 实例,并将其Job实例化的工厂指定为一个Spring容器中一个自定义的 TaskSchedulerFactory 实例。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
 
@Configuration
public class QuartzConfig {
    @Autowired
    private QuartzJobFactory jobFactory;
 
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(jobFactory);
        return schedulerFactoryBean;
    }
 
    @Bean
    public Scheduler scheduler() {
        return schedulerFactoryBean().getScheduler();
    }
}

Quartz为我们提供了一个JobFactory接口,允许我们自定义实现创建Job的逻辑。

package org.quartz.spi;
public interface JobFactory {
    Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException;
}

AdaptableJobFactory 就是 Spring 为我们提供的一个该接口的实现类,提供了创建Job实例的基本方法。

自定义的工厂类 QuartzJobFactory 只需要继承 AdaptableJobFactory ,通过调用父类 AdaptableJobFactory 的方法来实现对Job的实例化,在Job实例化完以后,再调用自身方法为创建好的Job实例进行属性自动装配并将其纳入到Spring容器的管理之中。

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
 
@Component
public class QuartzJobFactory extends AdaptableJobFactory
{
 
    // 需要使用这个BeanFactory对Qurartz创建好Job实例进行后续处理,属于Spring的技术范畴.
    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;
 
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception
    {
        // 首先,调用父类的方法创建好Quartz所需的Job实例
        Object jobInstance = super.createJobInstance(bundle);
        // 然后,使用BeanFactory为创建好的Job实例进行属性自动装配并将其纳入到Spring容器的管理之中,属于Spring的技术范畴.
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

参考文献:

https://blog.csdn.net/pengjunlee/article/details/78965877 (SpringBoot重点详解--如何为Quartz的Job自动装配Spring容器Bean)

https://blog.csdn.net/qq_43419029/article/details/92659690

posted @ 2024-05-17 11:31  雨后观山色  阅读(125)  评论(0编辑  收藏  举报