Spring Boot: Using a @Service in Quartz job execution

让人想犯罪 __ 提交于 2019-12-20 12:39:01

问题


In an application, since I converted it from a classical Spring webapp (deployed in a system Tomcat) to a Spring Boot (V1.2.1) application I face the problem that the Quartz-based scheduled jobs are not working anymore.

I schedule these Quartz jobs like this:

// My own Schedule object which holds data about what to schedule when
Schedule schedule = scheduleService.get(id of the schedule);

String scheduleId = schedule.getId();

JobKey jobKey = new JobKey(scheduleId);
TriggerKey triggerKey = new TriggerKey(scheduleId);

JobDataMap jobData = new JobDataMap();
jobData.put("scheduleId", scheduleId);

JobBuilder jobBuilder = JobBuilder.newJob(ScheduledActionRunner.class)
    .withIdentity(jobKey)
    .withDescription(schedule.getName())
    .usingJobData(jobData);

JobDetail job = jobBuilder.build();

TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger()
    .forJob(jobKey)
    .withIdentity(triggerKey)
    .withDescription(schedule.getName());

triggerBuilder = triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(schedule.toCronExpression()));

Trigger trigger = triggerBuilder.build();

org.quartz.Scheduler scheduler = schedulerFactoryBean.getScheduler();

scheduler.scheduleJob(job, trigger);

ScheduledActionRunner:

@Component
public class ScheduledActionRunner extends QuartzJobBean {

    @Autowired
    private ScheduleService scheduleService;

    public ScheduledActionRunner() {
    }

    @Override
    public void executeInternal(final JobExecutionContext context) throws JobExecutionException {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
        final JobDataMap jobDataMap = context.getMergedJobDataMap();
        final String scheduleId = jobDataMap.getString("scheduleId");
        final Schedule schedule = scheduleService.get(scheduleId);
        // here it goes BANG since scheduleService is null
    }
}

ScheduleService is a classical Spring service which fetches data from Hibernate. As I said above, this worked fine until I moved to Spring Boot.

When I implemented this code with the classical Spring application, SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); did the trick to take care of autowiring the service.

What is needed to make this work again in the Spring Boot environment ?

Edit:

At the end I chose to move away from using Quartz in favour of Spring's ThreadPoolTaskScheduler.The code was much simplified and it works as expected.


回答1:


The SpringBeanAutowiringSupport uses the web application context, which is not available in your case. If you need a spring managed beans in the quartz you should use the quartz support provided by spring. This will give you full access to all the managed beans. For more info see the quartz section at spring docs at http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html. Also see following example of usage quartz with spring managed beans. Example is based on your code. So you can change the first code snippet (where the quartz initialization is done) with follwoing spring alternatives.

Create job detail factory

@Component
public class ScheduledActionRunnerJobDetailFactory extends JobDetailFactoryBean {

    @Autowired
    private ScheduleService scheduleService;

    @Override
    public void afterPropertiesSet() {
       setJobClass(ScheduledActionRunner.class);
       Map<String, Object> data = new HashMap<String, Object>();
       data.put("scheduleService", scheduleService);
       setJobDataAsMap(data);
       super.afterPropertiesSet();
   }
}

Create the trigger factory

@Component
public class ActionCronTriggerFactoryBean extends CronTriggerFactoryBean {

   @Autowired
   private ScheduledActionRunnerJobDetailFactory jobDetailFactory;

   @Value("${cron.pattern}")
   private String pattern;

   @Override
   public void afterPropertiesSet() throws ParseException {
       setCronExpression(pattern);
       setJobDetail(jobDetailFactory.getObject());
       super.afterPropertiesSet();
   }

}

And finally create the SchedulerFactory

@Component
public class ActionSchedulerFactoryBean extends SchedulerFactoryBean {

   @Autowired
   private ScheduledActionRunnerJobDetailFactory jobDetailFactory;

   @Autowired
   private ActionCronTriggerFactoryBean triggerFactory;

   @Override
   public void afterPropertiesSet() throws Exception {
       setJobDetails(jobDetailFactory.getObject());
       setTriggers(triggerFactory.getObject());
       super.afterPropertiesSet();
   }

}



回答2:


My answer not fully matches to you question, but Spring expose you another ability - to start cron-expression based scheduler on any service.

Using Spring.Boot you can configure your application to use scheduler by simple placing

@EnableScheduling
public class Application{
....

After that just place following annotation on public(!) method of @Service

@Service
public class MyService{
...
    @Scheduled(cron = "0 * * * * MON-FRI")
    public void myScheduledMethod(){
    ....
    }


来源:https://stackoverflow.com/questions/28134883/spring-boot-using-a-service-in-quartz-job-execution

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!