Autowiring in Quartz multiple Jobs with Spring Boo

2019-06-21 08:09发布

I'm try to use quartz scheduler in spring. I get below exception when configuring multiple jobs

Parameter 0 of method jobTrigger in Job2 required a bean of type 'org.quartz.JobDetail' that could not be found.

quartz - v2.3, Spring - v4.2.x

Config Class

@Configuration
 public class SchedulerConfig {

private static final Logger LOG = LoggerFactory.getLogger(SchedulerConfig.class);

@Autowired
List<Trigger> triggers;

@Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
    AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
    jobFactory.setApplicationContext(applicationContext);
    return jobFactory;
}

@Bean
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory)  throws IOException {
    SchedulerFactoryBean factory = new SchedulerFactoryBean();
          factory.setAutoStartup(true);
    factory.setJobFactory(jobFactory);
         factory.setQuartzProperties(quartzProperties());
    if (triggers != null && !triggers.isEmpty()) {
         LOG.info("starting jobs... Total Triggers - " + triggers.size());
        factory.setTriggers(triggers.toArray(new Trigger[triggers.size()]));
    }

    return factory;
}

@Bean
public Properties quartzProperties() throws IOException {
    PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
    propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
    propertiesFactoryBean.afterPropertiesSet();
    return propertiesFactoryBean.getObject();
}


public static CronTriggerFactoryBean createCronTrigger(JobDetail jobDetail, String cronExpression) {
    CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
    factoryBean.setJobDetail(jobDetail);
    factoryBean.setCronExpression(cronExpression);
    factoryBean.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
    return factoryBean;
}

public static JobDetailFactoryBean createJobDetail(Class jobClass) {
    JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
    factoryBean.setJobClass(jobClass);
    factoryBean.setDurability(true);
    return factoryBean;
}

SpringBeanJobFactory

public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {

private static final Logger LOG = LoggerFactory.getLogger(AutowiringSpringBeanJobFactory.class);

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);
    LOG.info("create job instance");
    beanFactory.autowireBean(job);
    return job;
}

}

Job 1

@Component
@DisallowConcurrentExecution
public class Job1 implements Job {

private final Logger log = LoggerFactory.getLogger(this.getClass());

@Value("${schedule}")
private String frequency;

@Autowired
private Service service;

@Override
public void execute(JobExecutionContext jobExecutionContext) {
    log.info("execute");
}

@Bean(name = "jobBean1")
public JobDetailFactoryBean job() {
    return SchedulerConfig.createJobDetail(this.getClass());
}

@Bean(name = "jobBean1Trigger")
public CronTriggerFactoryBean jobTrigger(@Qualifier("jobBean1")JobDetail jobDetail) {
    return SchedulerConfig.createCronTrigger(jobDetail, frequency);
}

Job 2

@Component
@DisallowConcurrentExecution
public class Job2 implements Job {

private final Logger log = LoggerFactory.getLogger(this.getClass());

@Value("${schedule}")
private String frequency;

@Autowired
private Service service;

@Override
public void execute(JobExecutionContext jobExecutionContext) {
    log.info("execute");
}

@Bean(name = "jobBean2")
public JobDetailFactoryBean job() {
    return SchedulerConfig.createJobDetail(this.getClass());
}

@Bean(name = "jobBean2Trigger")
public CronTriggerFactoryBean jobTrigger(@Qualifier("jobBean2")JobDetail jobDetail) {
    return SchedulerConfig.createCronTrigger(jobDetail, frequency);
}

The Service class has Spring JPA repos. The root cause of the problem is the below autowired service. If I remove the below autowired service from both the jobs, it works fine.

@Autowired private Service service;

If there is only one job with this autowired bean, then there is no exception. How to configure multiple jobs using the same autowired dependency? What is cause this issue?

4条回答
爷、活的狠高调
2楼-- · 2019-06-21 08:13

jobTrigger methods expect as argument a JobDetail but passed beans are of type JobDetailFactoryBean.

Maybe you should make the following change or something similar.

@Bean(name = "jobBean1")
public JobDetail job() {
    return SchedulerConfig.createJobDetail(this.getClass()).getObject();
}

And the same for job2.

By the way, you mention Spring - v1.5. Which version of Spring do you actually use?

查看更多
兄弟一词,经得起流年.
3楼-- · 2019-06-21 08:20

You are trying to use spring boot configuration with Spring 4.2.

Try changing the following methods inside Job class as follows

@Bean(name = "jobBean1")
public JobDetail job() {
    return SchedulerConfig.createJobDetail(this.getClass()).getObject();
}

@Bean(name = "jobBean1Trigger")
public CronTrigger jobTrigger(@Qualifier("jobBean1")JobDetail jobDetail) {
    return SchedulerConfig.createCronTrigger(jobDetail, frequency).getObject();
}

Also use spring 4.3 since you need

@Autowired
List<Trigger> triggers;

I believe Collection Autowire works only in 4.3

查看更多
Root(大扎)
4楼-- · 2019-06-21 08:30

This is a modified version of the http://www.baeldung.com/spring-quartz-schedule that you referenced for handling multiple Quartz Jobs in one configuration file. For brevity I'm not including the entire QrtzSheduler class just the replacement for the scheduler method and the use of @Qualifier reference in the Triggers:

...
@Bean
public Scheduler scheduler(Map<String, JobDetail> jobMap, Set<? extends Trigger> triggers) throws SchedulerException, IOException {

    StdSchedulerFactory factory = new StdSchedulerFactory();
    factory.initialize(new ClassPathResource("quartz.properties").getInputStream());

    logger.debug("Getting a handle to the Scheduler");
    Scheduler scheduler = factory.getScheduler();
    scheduler.setJobFactory(springBeanJobFactory());
    Map<JobDetail,Set<? extends Trigger>> triggersAndJobs = new HashMap<JobDetail,Set<? extends Trigger>>;
    for(JobDetail jobDetail : jobMap.getValues()){
        for(Trigger trigger : triggers){
            if(trigger.getJobKey().equals(jobDetail.getKey())){
               Set<Trigger> set = new HashSet<>();
               set.add(trigger);
               triggerAndJobs.put(jobDetail,set);
            }
        }
    }
    scheduler.scheduleJobs(triggersAndJobs, false);

    logger.debug("Starting Scheduler threads");
    scheduler.start();
    return scheduler;
}

@Bean(name="jobOne")
public JobDetail jobDetailOne() {

    ...
}

@Bean(name="jobTwo")
public JobDetail jobDetailTwo() {

   ...
}

@Bean
public Trigger triggerOne(@Qualifier("jobOne")JobDetail jobDetail) {

   ...
}

@Bean
public Trigger triggerTwo(@Qualifier("jobTwo")JobDetail jobDetail) {

   ...
}
查看更多
冷血范
5楼-- · 2019-06-21 08:30

I faced the same issue and after a bit of struggling I was able to resolve it. It is probably realted to the annotation that is used in the job class. I see that you are using the @Component, as it was in my case. The quartz scheduler configuration instead, is annotated with @Configuration tag.

The solution is to annotate your jobs with @Configuration tag. My guess is that the @Bean's constructed in @Componentannotated classes are not completley constructed/mapped in the phase where @Configuration annotated ones are initialized.

查看更多
登录 后发表回答