环境
- SpringBoot 2.6.4
- Quartz 2.3.2
现象
在JOB对象中,使用@Autowire注解注入spring的bean对象,抛出空指针异常;
源码分析
scheduler
使用SchedulerFactoryBean
对象获取,获取方式是
Scheduler scheduler = schedulerFactoryBean.getScheduler();
因为springBoot集成了spring-boot-starter-quartz
,因此可以在QuartzAutoConfiguration
配置类中找到SchedulerFactoryBean
的定义,如下所示:
@Bean
@ConditionalOnMissingBean
public SchedulerFactoryBean quartzScheduler(QuartzProperties properties,
ObjectProvider<SchedulerFactoryBeanCustomizer> customizers, ObjectProvider<JobDetail> jobDetails,
Map<String, Calendar> calendars, ObjectProvider<Trigger> triggers, ApplicationContext applicationContext) {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
SpringBeanJobFactory jobFactory = new SpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
schedulerFactoryBean.setJobFactory(jobFactory);
//省略其他代码
return schedulerFactoryBean;
}
通过上述代码可以看到SchedulerFactoryBean
设置类工厂类SpringBeanJobFactory
,进入该类可以看到该类继承了AdaptableJobFactory
类,并且实现了ApplicationContextAware
接口,查看该类的创建job实例的方法,如下所示:
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object job = this.applicationContext != null ? this.applicationContext.getAutowireCapableBeanFactory().createBean(bundle.getJobDetail().getJobClass(), 3, false) : super.createJobInstance(bundle);
if (this.isEligibleForPropertyPopulation(job)) {
//省略其他代码
}
return job;
}
该方法中通过applicationContext
获取到AutoWireCapableBeanFactory
,进而去创建bean,也即将创建的JOB对象同时注入到spring容器中,那么也就是说在该版本的springboort中,默认情况下在JOB中注入bean是可以实现的。
不能注入原因
按照上面的分析,默认情况下,在job中注入bean是可以正常使用的,那么为什么还是出现了NullPonterException呢?原因在于项目中因为要设置Quarz的数据源,重新定义了SchedulerFactoryBean
,原始代码如下:
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
return factory;
}
这就覆盖了QuartzAutoConfiguration
配置中的原始定义,此时的JOB是没有交给spring容器管理的,也就导致了注入其他bean时会出现空指针;
解决方案
在重写的配置类中,添加SpringBeanJobFactory
对象,如下所示:
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
//关键代码
SpringBeanJobFactory jobFactory = new SpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
factory.setJobFactory(jobFactory);
//关键代码
return factory;
}