3

Previously I had set it up to autowired in a quartz job.
Note here.

But, the autowired of the job inner class will fail.

My job code example is here.

public class MyJob extends QuartzJobBean {

    @Autowired
    private Hello hello; //<--- this is suceess!

    @Override
    public void executeInternal(JobExecutionContext context) {
         //...

         Do do = new Do();
         do.doSomething();

         //...
    }
}

Do.java

public class Do {
    @Autowired
    priavte Do2 do2; // <---- ***this is null !***

    //...                
}

Why is this happening?
How do I solve it and what concepts should I know more?

taeun
  • 95
  • 1
  • 6

1 Answers1

4

Quartz jobs are not ran in the same context as spring so autowired objects become null within the same class. You have to make Quartz Spring aware.

First add sprint-context-support

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>${spring.version}</version>
</dependency>

Then create a Application Context Holder that is Application Context Aware

@Component
public final class ApplicationContextHolder extends SpringBeanJobFactory implements ApplicationContextAware {

  private static ApplicationContext context;

  private transient AutowireCapableBeanFactory beanFactory;

  @Override
  public void setApplicationContext(ApplicationContext ctx) throws BeansException {
    beanFactory = ctx.getAutowireCapableBeanFactory();
    context = ctx;
  }

  @Override
  protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
    final Object job = super.createJobInstance(bundle);
    beanFactory.autowireBean(job);
    return job;
  }

  public static ApplicationContext getContext() {
    return context;
  }
}

Then you can create your Quartz Scheduler Configuration Class

@Configuration
public class QuartzSchedulerConfiguration {

  @Autowired
  private ApplicationContext applicationContext;

  /**
   * Create the job factory bean
   * @return Job factory bean
   */
  @Bean
  public JobFactory jobFactory() {
    ApplicationContextHolder jobFactory = new ApplicationContextHolder();
    jobFactory.setApplicationContext(applicationContext);
    return jobFactory;
  }

  /**
   * Create the Scheduler Factory bean
   * @return scheduler factory object
   */
  @Bean
  public SchedulerFactoryBean schedulerFactory() {
    SchedulerFactoryBean factory = new SchedulerFactoryBean();
    factory.setAutoStartup(true);
    factory.setSchedulerName("My Scheduler");
    factory.setOverwriteExistingJobs(true);
    factory.setJobFactory(jobFactory());
    return factory;
  }
}

Now this will place your quartz scheduler in the same context as Spring, so you can now create a SchedulerService class.

@Service
public class SchedulerService {

  @Autowired
  private SchedulerFactoryBean schedulerFactory;

  private Scheduler scheduler;

  /**
   * Initialize the scheduler service
   */
  @PostConstruct
  private void init() {
    scheduler = schedulerFactory.getScheduler();
  }
}

now you can populate this class with methods to create your schedules using the scheduler object and when the task is triggered the class that extends Job will be context aware with spring and the autowired objects will no longer be null

to address the follow up question implement the ApplicationContextHolder component then autowire it into your SchedulerConfig class

@Autowire
ApplicationContextHolder holder

@Bean
// injecting SpringLiquibase to ensure liquibase is already initialized and created the quartz tables: 
public JobFactory jobFactory(SpringLiquibase springLiquibase) {
  AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
  jobFactory.setApplicationContext(holder);
  return jobFactory;
}
locus2k
  • 2,802
  • 1
  • 14
  • 21
  • hello, thank you for your answer. However, I do not want to use the schedule in the service, but the problem is that it is not autowired in the service used by the job. Do you know how to fix it? (Fixed link to my note above.) – taeun Feb 05 '18 at 06:02
  • The reason why you service jobs are not working is because quartz is not in the same context as spring that is why you need to create the application context aware code to allow quartz to be aware of spring and be in the same context. I believe if you implement the ApplicationContextHolder component that i provided above then autowire it in your SchedulerConfig that should do it. See my edit. – locus2k Feb 05 '18 at 13:55
  • @locus2k, I'm also facing similar issue with springboot 2.6.4 but your answer could not solve my issue. My autowired objects are still null. Can you please help me? – Ravi Aug 01 '22 at 11:53
  • @Ravi I can look into it with the latest version to try to get it to work. This was 4 years ago so the libraries may have changed – locus2k Sep 17 '22 at 03:45