在使用Quart定时任务框架时,自定义Job类不支持@Autowired。往Job任务类中注入对象后,通过注入对象调用方法发现,运行出现异常。原来,并没有注入成功!
JobDetailFactoryBean
的setJobClass()
方法底层调用的是AdaptableJobFactory
的createJobInstance()
方法。该方法是通过反射创建的对象。
看源码:
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Class<?> jobClass = bundle.getJobDetail().getJobClass();
return ReflectionUtils.accessibleConstructor(jobClass, new Class[0]).newInstance();//反射创建对象
}
也就是说,这时候创建我们自定义的Job类时并没有用到spring容器。即spring容器中没有我们的Job类对象。
而spring的依赖注入要求注入对象和被注入对象都要在容器中,所以当我们在给Job类注入对象时是注入不成功的!
那怎么解决呢?
解决思路:
我们可以写一个类继承AdaptableJobFactory
并重写createJobInstance
方法,创建完对象后我们手动地给他放到spring容器中。
---------------------------------------这是一条华丽的分割线-------------------------------------------
先来看看我的Job类。
定时任务的Job:
给自定义Job类注入对象(TestService)。能注入成功吗?不能!为什么?
public class MyJob implements Job {
//给Job注入对象(TestService)。能成功吗?不能!为什么?
@Autowired
private TestService testService;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date());
System.out.println(" 定时任务:" + printTime + "开始, prints: Hello Job-" + new Random().nextInt(100));
testService.addUser();//调用注入对象的方法
}
}
Quartz配置类:
@Configuration
public class QuartzConfig {
//1.配置JobDetail任务对象
@Bean
public JobDetailFactoryBean jobDetailFactoryBean(){
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
//关联自己的job类
factoryBean.setJobClass(MyJob.class);
return factoryBean;
}
//2.配置Trigger触发器 。略
//3.配置Scheduler调度器 。略
解决:
写一个类继承AdaptableJobFactory
并重写createJobInstance
方法
@Component
public class MyAdaptableJobFactory extends AdaptableJobFactory {
// AutowireCapableBeanFactory就可以将一个对象添加到spring容器中,并且完成该对象的属性注入。
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
/**
* 该方法需要将实例化的任务对象手动地添加到spring容器中并完成对象的注入
*/
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception{
Object obj = super.createJobInstance(bundle);
//将obj对象添加到spring容器中并完成属性注入
autowireCapableBeanFactory.autowireBean(obj);
return obj;
}
}
Quartz配置: 配置调度器时传入我们刚刚写的类MyAdaptableJobFactory
//前几步代码.略
//3.配置Scheduler调度器
//给
@Bean
public SchedulerFactoryBean schedulerFactoryBean(CronTriggerFactoryBean cronTriggerFactoryBean,MyAdaptableJobFactory myAdaptableJobFactory){
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
//关联trigger
factoryBean.setTriggers(cronTriggerFactoryBean.getObject());
factoryBean.setJobFactory(myAdaptableJobFactory);//重新设置创建Job实例的工厂类
return factoryBean;
}
这样就可以完成属性注入啦!!!!
但是感觉这样有点麻烦,我完全可以把我写这个类在Quartz配类里配置,这样一个文件就搞定了。
优化:Quartz配置
/**
* Quartz配置类
*/
@Configuration
public class QuartzConfig {
@Autowired
private ApplicationContext applicationContext;
/**
* 扩展SpringBeanJobFactory,使Job实现类支持@autowired注入
*/
private 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;
}
}
//1.配置JobDetail任务对象
@Bean
public JobDetailFactoryBean jobDetailFactoryBean(){
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
//关联自己的job类
factoryBean.setJobClass(PrintWordsJob.class);
return factoryBean;
}
//2.配置Trigger触发器
@Bean
public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
factoryBean.setJobDetail(jobDetailFactoryBean.getObject());
//设置时间
factoryBean.setCronExpression("0/2 * * * * ?");//每两秒触发一次
return factoryBean;
}
//3.配置Scheduler调度器
@Bean
public SchedulerFactoryBean schedulerFactoryBean(CronTriggerFactoryBean cronTriggerFactoryBean){
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
//关联trigger
factoryBean.setTriggers(cronTriggerFactoryBean.getObject());
AutowiringSpringBeanJobFactory autowiringSpringBeanJobFactory = new AutowiringSpringBeanJobFactory();
factoryBean.setJobFactory(autowiringSpringBeanJobFactory);
return factoryBean;
}
/*配置完了怎么启动呢?给springboot启动类加@EnableShceduling注解*/
}
---------------------天大寒,砚冰坚,手指不可屈伸,弗之怠。--------------------------