Reputation: 6116
I have a spring boot project that I want to use quartz with to run certain jobs at certain times. I have this class layout:
abstract public class AbstractFoo {
protected final FooB fooB;
public AbstractFoo(FooB fooB) {
this.fooB = fooB;
}
}
@Service
public class SomeJob extends AbstractFoo implements Job {
public SomeJob(FooB fooB) {
super(fooB);
}
@Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
//do stuff
}
}
However, when I run this job I get the following error:
2017-12-06 14:18:01,383 ERROR --- [quartz-jobs] org.quartz.core.ErrorLogger : An error occured instantiating job to be executed. job= 'jobGroup.someJob'
org.quartz.SchedulerException: Job instantiation failed
at org.springframework.scheduling.quartz.AdaptableJobFactory.newJob(AdaptableJobFactory.java:45)
at org.quartz.core.JobRunShell.initialize(JobRunShell.java:127)
at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:375)
Caused by: java.lang.InstantiationException: com.jobs.SomeJob
at java.lang.Class.newInstance(Class.java:427)
at org.springframework.scheduling.quartz.AdaptableJobFactory.createJobInstance(AdaptableJobFactory.java:58)
at org.springframework.scheduling.quartz.SpringBeanJobFactory.createJobInstance(SpringBeanJobFactory.java:74)
at com.config.AutowiringSpringBeanJobFactory.createJobInstance(AutowiringSpringBeanJobFactory.java:27)
at org.springframework.scheduling.quartz.AdaptableJobFactory.newJob(AdaptableJobFactory.java:41)
... 2 common frames omitted
Caused by: java.lang.NoSuchMethodException: com.jobs.SomeJob.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 6 common frames omitted
I already have an autowire factory like so:
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;
}
}
And here's my quartz config:
@Configuration
public class QuartzConfig {
@Autowired
private DataSource dataSource;
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private ApplicationContext applicationContext;
@Bean
public SchedulerFactoryBean quartzScheduler() {
SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean();
quartzScheduler.setDataSource(dataSource);
quartzScheduler.setTransactionManager(transactionManager);
quartzScheduler.setOverwriteExistingJobs(true);
quartzScheduler.setSchedulerName("quartz-jobs");
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
quartzScheduler.setJobFactory(jobFactory);
quartzScheduler.setQuartzProperties(quartzProperties());
Trigger[] triggers = {someJobTrigger().getObject();
quartzScheduler.setTriggers(triggers);
return quartzScheduler;
}
@Bean
public CronTriggerFactoryBean someJobTrigger() {
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
cronTriggerFactoryBean.setJobDetail(someJob().getObject());
cronTriggerFactoryBean.setCronExpression(cronExp);
cronTriggerFactoryBean.setGroup(someGroup);
return cronTriggerFactoryBean;
}
@Bean
public JobDetailFactoryBean someJob() {
JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean();
jobDetailFactory.setJobClass(SomeJob.class);
jobDetailFactory.setGroup(someGroup);
jobDetailFactory.setDurability(true);
return jobDetailFactory;
}
@Bean
public Properties quartzProperties() {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
Properties properties = null;
try {
propertiesFactoryBean.afterPropertiesSet();
properties = propertiesFactoryBean.getObject();
} catch (IOException e) {
}
return properties;
}
}
How do I get Quartz to wire in the appropriate dependencies through the constructor?
Upvotes: 7
Views: 9038
Reputation: 1396
This problem occures when class not contains default constructor.
Example : YourJob extends QuartzJobBean then class should contains default constructor. Add @Component adnotation and @NoArgsConstructor if you use lombok. Other required beans should be autowired to this class by @Autowired
Upvotes: 0
Reputation: 5786
I just posted an answer here with just normal quartz with autowiring capability. However, please note that you need to have a no-args constructor(Use both no-args and arg constructor in your case). Quartz uses class name to instantiate instance and so, your constructor with argument wont work.
If you still are working on the same problem, try my approach where you can auto wire the required dependency instead of going via super(foo);
https://stackoverflow.com/a/49316580/2931410
Upvotes: 1
Reputation: 1050
As stated in docs:
One of the ramifications of this behavior is the fact that jobs must have a no-argument constructor (when using the default JobFactory implementation).
You essentially using default JobFactory with autowiring capability added. What I found from my personal tests is that autowiring will not work with constructor injection. Also, don't mark your job with Spring annotations (Component, Service, e.c.t) as it has no effect.
To solve your problem, refactor your job to have default constructor and autowire dependencies with field injection (maybe setter injection will work too).
abstract public class AbstractFoo {
@Autowired
protected FooB fooB;
}
public class SomeJob extends AbstractFoo implements Job {
@Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
//do stuff
}
}
Upvotes: 13