quartz无法注入springbean的原因

参考:https://zhuanlan.zhihu.com/p/146060950

如何解决 Quartz Job 中无法注入 Spring Bean
xkcoding
xkcoding

Later Equals Never

  1. 问题

不知道小伙伴们在 Spring 集成 Quartz 的时候有没有遇到过这样一个问题,就是在 Quartz 的 Job 中 @Autowired 一个 Spring Bean 的时候会报空指针异常。如果在 Spring 中无法使用 @Autowired 进行注入一个 Bean 的话,这无疑是一个噩耗,那么遇到这个问题,该如何解决呢?
2. 原因

出现这个问题是因为定时任务的 Job 对象实例化的过程是通过 Quartz 内部自己完成的,但是我们通过 Spring 进行注入的 Bean 却是由 Spring 容器管理的,Quartz 内部无法感知到 Spring 容器管理的 Bean,所以没有办法在创建 Job 的时候就给装配进去。
3. 源码分析

传统的 Spring 项目,我们可以看到 Schedule 的创建是通过 SchedulerFactoryBean 进行创建的,我们看一下 SchedulerFactoryBean 源码,该类实现了 InitializingBean 接口,会在 Bean 属性初始化之后调用 afterPropertiesSet() 方法。

@Override
public void afterPropertiesSet() throws Exception {
// 省略部分代码…

// Initialize the Scheduler instance…
this.scheduler = prepareScheduler(prepareSchedulerFactory());
// 省略部分代码…
}

接着查看 prepareScheduler() 方法,可以发现如果 jobFactory 不存在的话,默认会使用 AdaptableJobFactory 实现对 Job 对象的创建。

private Scheduler prepareScheduler(SchedulerFactory schedulerFactory) throws SchedulerException {
// 省略部分代码…

// Get Scheduler instance from SchedulerFactory.
try {
Scheduler scheduler = createScheduler(schedulerFactory, this.schedulerName);
populateSchedulerContext(scheduler);

  if (!this.jobFactorySet && !(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.applicationContext != null && this.jobFactory instanceof ApplicationContextAware) {
        ((ApplicationContextAware) this.jobFactory).setApplicationContext(this.applicationContext);
     }
     if (this.jobFactory instanceof SchedulerContextAware) {
        ((SchedulerContextAware) this.jobFactory).setSchedulerContext(scheduler.getContext());
     }
     scheduler.setJobFactory(this.jobFactory);
  }
  return scheduler;

}

// 省略部分代码…
}

既然找到了源码,那么处理起来就方便了,我们如果可以自定义 JobFactory 的话,在创建完 Job 实例之后,再将 Job 注入到 Spring 容器中即可解决该问题。
4. 解决

首先自定义一个 JobFactory,通过 AutowireCapableBeanFactory 将创建好的 Job 对象交给 Spring 管理

@Configuration
public class CustomJobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;

/**
 * Create the job instance, populating it with property values taken
 * from the scheduler context, job data map and trigger data map.
 *
 * @param bundle
 */
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
    Object jobInstance = super.createJobInstance(bundle);
    autowireCapableBeanFactory.autowireBean(jobInstance);
    return jobInstance;
}

}

再创建一个配置类,将自定义的 JobFactory 设置到 Schedule 中

@Configuration
public class QuartzConfig {
@Autowired
private CustomJobFactory customJobFactory;

@SneakyThrows
@Bean
public Scheduler scheduler(){
    SchedulerFactory schedulerFactory = new StdSchedulerFactory();
    Scheduler scheduler = schedulerFactory.getScheduler();
    // 自定义 JobFactory 使得在 Quartz Job 中可以使用 @Autowired
    scheduler.setJobFactory(customJobFactory);
    scheduler.start();
    return scheduler;
}

}

这样你就可以愉快的在定时任务中使用 @Autowired注入 Spring 管理的 Bean 了。

@Slf4j
public class JobDemo2 implements Job {
@Autowired
private DemoService demoService;

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    demoService.echo("JobDemo2");
}

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值