简体   繁体   中英

Spring Quartz Setting up Constructor based Injection with Parent Class Constructor

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?

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, ect) 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
    }
}

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM