Quartz 注入 Spring Bean爲空

問題描述

項目中採用了Quartz來執行調度任務,但在 Quartz自動調用 Job任務時,發現在JobBean中使用@Autowired進行依賴注入會產生null指針錯誤
經查閱資料得知,大致原因爲Job由Quartz實例化創建,因而在Job中使用依賴注入時無法找到Spring的Bean
開發所使用的版本號

SpringBoot:2.1.5.RELEASE
quartz:2.3.0

解決方案

經過我的研究,無論是好壞的方法共有下面幾種,都已經過測試。【這裏我推薦第一種,更加符合Spring的要求】

一、使用SpringBeanJobFactory,將Job對象與Spring對象關聯

先創建一個Factory,該方法繼承SpringBeanJobFactory並實現ApplicationContextAware
通過autowireBean方法將Quartz實例化後的Job添加到Spring的Bean中

public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
        ApplicationContextAware {
    private transient AutowireCapableBeanFactory beanFactory;

    @Override
    public void setApplicationContext(final ApplicationContext context) {
        beanFactory = context.getAutowireCapableBeanFactory();
    }
	
    @Override
    protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
        final Object job = super.createJobInstance(bundle);
        beanFactory.autowireBean(job);
        return job;
    }
}

然後,將其附加到ScheduleConfig

 	@Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
        jobFactory.setApplicationContext(SpringContextUtils.applicationContext);
        factory.setJobFactory(jobFactory);
        
        ...........
    }

其中SpringContextUtils爲自定義的Spring Context 工具類

@Component
public class SpringContextUtils implements ApplicationContextAware {
    public static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtils.applicationContext = applicationContext;
    }
}

推薦優先使用該方式


二、在Servlet中調用SpringBeanAutowiringSupport

使用當前方法需要Spring-Web版本大於 2.5.1
將其作爲Job的第一行即可

public class BatchJob extends Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
        
        ...........
    }
}

該方式雖可行,但不利於多個Job的情況


三、直接使用工具類獲取Bean

改造第一種方法中的工具類如下,增加獲取bean的方法

@Component
public class SpringContextUtils implements ApplicationContextAware {
    public static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtils.applicationContext = applicationContext;
    }

    /**
     * 獲取Bean
     * @param name bean名稱
     */
    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }
}

之後在需要使用Bean的地方直接使用工具類獲取。例如

public class BatchJob extends Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
        // 需要獲取的Bean
        JobService jobService = (JobService) SpringContextUtils.getBean("jobService");
        ...........
    }
}

當前方式簡單但與第二個方法大同小異,都不利於多種任務的情況

總結

嚴格來說還有其他的方式,比如繼承QuartzJobBean,但實際驗證後並無作用,因此目前就列舉以上三種,各有優缺點,可根據需求自行選擇。
另外作者才疏學淺,若有疏漏或不對的地方敬請諒解!
轉載請註明出處!!!

參考

stackoverflow
SpringBeanAutowiringSupport介紹

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章