問題描述
項目中採用了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,但實際驗證後並無作用,因此目前就列舉以上三種,各有優缺點,可根據需求自行選擇。
另外作者才疏學淺,若有疏漏或不對的地方敬請諒解!
轉載請註明出處!!!