java Spring Boot:在 Quartz 作业执行中使用 @Service

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28134883/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 13:02:53  来源:igfitidea点击:

Spring Boot: Using a @Service in Quartz job execution

javaspringhibernatespring-bootquartz-scheduler

提问by yglodt

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.

在应用程序中,由于我将它从经典的 Spring webapp(部署在系统 Tomcat 中)转换为 Spring Boot (V1.2.1) 应用程序,我面临基于 Quartz 的计划作业不再工作的问题。

I schedule these Quartz jobs like this:

我这样安排这些 Quartz 作业:

// 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:

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
    }
}

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

ScheduleService是一个经典的 Spring 服务,它从 Hibernate 中获取数据。正如我上面所说,在我转向 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.

当我用经典的 Spring 应用程序实现这段代码时,SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);就完成了自动装配服务的技巧。

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

需要什么才能在 Spring Boot 环境中再次工作?

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.

最后我选择放弃使用 Quartz,转而使用 Spring 的 ThreadPoolTask​​Scheduler。代码大大简化,并且按预期工作。

回答by Babl

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.

SpringBeanAutowiringSupport 使用 Web 应用程序上下文,这在您的情况下不可用。如果您需要在石英中使用 spring 管理的 bean,您应该使用 spring 提供的石英支持。这将使您能够完全访问所有托管 bean。有关更多信息,请参阅http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html上的 spring 文档中的石英部分。另请参阅以下使用石英与 Spring 管理的 bean 的示例。示例基于您的代码。因此,您可以使用以下弹簧替代方案更改第一个代码片段(石英初始化完成的地方)。

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

最后创建 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();
   }

}

回答by Dewfy

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

我的回答与您的问题并不完全相符,但 Spring 向您展示了另一种能力 - 在任何服务上启动基于 cron 表达式的调度程序。

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

使用 Spring.Boot,您可以通过简单的放置将您的应用程序配置为使用调度程序

@EnableScheduling
public class Application{
....

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

之后,只需在public(!) 方法上放置以下注释@Service

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