问题描述
项目中采用了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,但实际验证后并无作用,因此目前就列举以上三种,各有优缺点,可根据需求自行选择。
另外作者才疏学浅,若有疏漏或不对的地方敬请谅解!
转载请注明出处!!!