How to trigger a scheduled Spring Batch Job?

后端 未结 4 1387
日久生厌
日久生厌 2021-02-06 00:51

I want to be able to start my job with a REST controller, then when the job is started, it should run on a scheduled basis, until i stop it again with REST.

So this is m

4条回答
  •  小鲜肉
    小鲜肉 (楼主)
    2021-02-06 01:42

    In this solution you will be able to schedule and unschedule pre defined jobs using http requests. In this example we will create a daily, weekly and an oneTime Job. The application is using Quartz.

    
    
        org.quartz-scheduler
        quartz
        2.2.3
    
    
        org.springframework
        spring-tx
    
    
        org.springframework
        spring-context-support
    
    

    First we have to create an AutowiringSpringBeanJobFactory class extends SpringBeanJobFactory.

    • Subclass of {@link AdaptableJobFactory} that also supports Spring-style * dependency injection on bean properties. This is essentially the direct * equivalent of Spring's {@link QuartzJobBean} in the shape of a Quartz * {@link org.quartz.spi.JobFactory}. * *

      Applies scheduler context, job data map and trigger data map entries * as bean property values. If no matching bean property is found, the entry * is by default simply ignored. This is analogous to QuartzJobBean's behavior.

    public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
    
        private transient AutowireCapableBeanFactory beanFactory;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            beanFactory = applicationContext.getAutowireCapableBeanFactory();        
        }
    
        @Override
        protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
            final Object job = super.createJobInstance(bundle);
            beanFactory.autowireBean(job);
            return job;
        }
    }
    

    The second part is to configure the quartz configuration. In this config we need to create a

    • SchedulerFactoryBean where we set global config and the application context,
    • JobDetailFactoryBean where we set our job, the jobGroup and the class,

    • CronTriggerFactoryBean where we set the cron expression.

    QuartzConfig.class

    @Configuration
    public class QuartzConfig {
    
        @Autowired
        ApplicationContext context;
    
        @Bean
        public SchedulerFactoryBean quartzScheduler(){
            SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean();
            quartzScheduler.setOverwriteExistingJobs(true);
            quartzScheduler.setSchedulerName("job-scheduler");
            AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
            jobFactory.setApplicationContext(context);
            quartzScheduler.setJobFactory(jobFactory);
            return quartzScheduler;
        }
    
        @Bean
        @Scope(value = "prototype")
        public JobDetailFactoryBean getJobBean(String jobName, String jobGroup, Class clazz){
            JobDetailFactoryBean bean = new JobDetailFactoryBean();
            bean.setJobClass(clazz);
            bean.setGroup(jobGroup);
            bean.setName(jobName);
            return bean;
        }
    
        @Bean
        @Scope(value = "prototype")
        public CronTriggerFactoryBean getCronTriggerBean(String cronExpression, String triggerGroup){
            CronTriggerFactoryBean bean = new CronTriggerFactoryBean();
            bean.setCronExpression(cronExpression);
            bean.setGroup(triggerGroup);
            return bean;
        }
    }
    

    So, after the config is done we are now able to create our jobs where the business logic will be placed. For that we have to create a class which implements Job.

    @Component
    public class DailyJob implements Job{
    
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("Daily Job runs!");
        }
    }
    

    The DailyJob class is now ready to get scheduled. We want to schedule this job from outside via a http request. In this example we have a controller where we can send the jobname and the cron expression to schedule the dailyJob.

    @Controller
    public class JobController {
    
        @Autowired
        private Scheduler scheduler;
        @Autowired
        private ApplicationContext context;;
    
        @ResponseBody
        @RequestMapping(value = "/job/create/daily", method = RequestMethod.POST)
        public ResponseEntity dailyJob(@RequestBody JobModel jobModel) throws SchedulerException {
            JobDetail jobDetail = context.getBean(
                    JobDetail.class, jobModel.getName(), "MyDailyJob", DailyJob.class);
            Trigger cronTrigger = context.getBean(
                    Trigger.class, jobModel.getCronExpression(), "MyDailyJob");
    
            scheduler.scheduleJob(jobDetail, cronTrigger);
    
            return new ResponseEntity(jobModel, HttpStatus.CREATED);
        }
    }
    

    What we see here is that we will send a post request with a JobModel as @RequestBody. JobModel is a simple Pojo with two attributes name and cronExpression both Strings.

    In this method we have to create the bean instances which we have configured previously in our config class. First create JobDetail with Quartz JobDetail.class, the name of your job, the name of the group and the Class which should be scheduled (in this case DailyJob.class). After that we have to create the Trigger with Quartz Trigger.class, the cronExpression and the group name.

    After both beans are created we need to schedule the job now. So we have autowired Quartz Scheduler to schedule the job. After that the job is enabled and ready to do its job.

    So let's test the stuff. Start the application and send a post request to /job/create/daily:

    {"name":"Job 1", "cronExpression":"0 * * * * ?"}
    

    Here we say that the job should run every minute (just to see that everything works). In your console you should see every minute Daily Job runs!.

    And here are some additional things you can do. For example get a list of the scheduled jobs:

     @ResponseBody
     @RequestMapping("job/list")
     public List jobList() throws SchedulerException {
         return scheduler.getJobGroupNames();
     }
    

    To delete a job you can create endpoints, too. For example:

    @ResponseBody
    @RequestMapping(value = "job/delete/daily", method = RequestMethod.POST)
    public ResponseEntity deleteJob(@RequestBody JobModel jobModel) throws SchedulerException {
        JobKey jobKey = new JobKey(jobModel.getName(), "MyDailyJob");
        return new ResponseEntity(scheduler.deleteJob(jobKey), HttpStatus.OK);
    }
    

    You are free to create many different endpoints to get informations about currently running jobs, how often jobs were running, reschedule jobs and so on. Important is just, that your jobname and the jobgroup( in our case "MyDailyJob") are reusable. Those information are needed to create the jobKey.

    P.S.: Just to show the other mappings for the other jobs:

    @ResponseBody
    @RequestMapping(value = "/job/create/weekly", method = RequestMethod.POST)
    public ResponseEntity weeklyJob(@RequestBody JobModel jobModel) throws SchedulerException {
        JobDetail jobDetail = context.getBean(JobDetail.class, jobModel.getName(), JobGroup.WEEKLY_GROUP.name(),
                WeeklyJob.class);
        Trigger cronTrigger = context.getBean(Trigger.class, jobModel.getCronExpression(),
                JobGroup.WEEKLY_GROUP.name());
    
        scheduler.scheduleJob(jobDetail, cronTrigger);
    
        return new ResponseEntity(jobModel, HttpStatus.CREATED);
    
    }
    
    @ResponseBody
    @RequestMapping(value = "/job/create/oneTime", method = RequestMethod.POST)
    public ResponseEntity oneTimeJob(@RequestBody JobModel jobModel) throws SchedulerException {
        JobDetail jobDetail = context.getBean(JobDetail.class, jobModel.getName(), JobGroup.ONE_TIME_GROUP.name(),
                OneTimeJob.class);
        Trigger cronTrigger = context.getBean(Trigger.class, jobModel.getCronExpression(),
                JobGroup.ONE_TIME_GROUP.name());
    
        scheduler.scheduleJob(jobDetail, cronTrigger);
    
        return new ResponseEntity(jobModel, HttpStatus.CREATED);
    }
    

    The full application is on github

提交回复
热议问题