How to create Spring Beans in a dynamical way. Using Quartz SchedulerFactoryBean

怎甘沉沦 提交于 2019-12-06 11:14:40

The reason why your list will contain null values is because the getObject method you are calling, should return the CronTrigger which is only initiated in afterPropertiesSet method called by spring when done initiating the spring context. You can call this method yourself manually on your CronTriggerFactoryBean, this will allow you to have it as a private method.

    // Just to clarify, no annotations here
    private CronTriggerFactoryBean jobTrigger(ChannelPartner ch, JobConfig jobConfig) throws ParseException {
        CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
        cronTriggerFactoryBean.setJobDetail(jobBean(ch, jobConfig).getObject());
        cronTriggerFactoryBean.setCronExpression(jobConfig.getSchedule());
        cronTriggerFactoryBean.setGroup("mainGroup");
        cronTriggerFactoryBean.setBeanName(jobConfig.getName() + "Trigger");
        cronTriggerFactoryBean.afterPropertiesSet();
        return cronTriggerFactoryBean;
    }

I'm sure there are many other ways of doing this as well, as you mentioned yourself you did a work-around for it, if this however is not what you want or need I can check some more if I can find a better way.

Your jobTrigger() and jobBean() methods are not actual beans but factory methods you are using given some inputs to construct CronTriggers and JobDetails to register in your loop found in your quartzScheduler bean by invoking triggers.add(..).

Remove the @Bean and @Scope annotations from the jobTrigger() and jobBean() methods (ideally reduce their visibility too (package private if not private) and you should be good to go.

After many different tries to get this code working, I found a working solution. Its just a workaround but gives maybe some hints to find the right - not workaround - solution.

What I did:

  1. I changed all my @Configuration classes to @Component except ChannelPartnerProperties and QuartzJobConfig.
  2. I put @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE) to my jobBean() and jobTrigger() method.
  3. I deleted the method parameter of both.
  4. I dont have any other @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE) anywhere else in my code.
  5. I created three counter for counting through my channelPartners, jobConfigs and one for the TriggerGroups name.
  6. I dont use the local Objects in my loops anymore. But use the counters to get the right Objects from my @Autowired channelPartnerProperties which holds all the entries of my yaml file.

After that my QuartzJobConfig class looks like that:

@Configuration
public class QuartzJobConfig implements IJobClass {

    private static int channelPartnerCount = 0;
    private static int jobCount = 0;
    private static int groupCounter = 0;

    @Autowired
    ChannelPartnerProperties channelPartnerProperties;

    @Autowired
    private ApplicationContext applicationContext;

    @Bean
    public SchedulerFactoryBean quartzScheduler() {
        SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean();

        quartzScheduler.setOverwriteExistingJobs(true);
        quartzScheduler.setSchedulerName("-scheduler");

        AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
        jobFactory.setApplicationContext(applicationContext);
        quartzScheduler.setJobFactory(jobFactory);

        List<CronTrigger> triggers = new ArrayList<>();
        for (ChannelPartner ch : channelPartnerProperties.getChannelPartners()) {
            for (JobConfig jobConfig : ch.getJobConfigs()) {
                triggers.add(jobTrigger().getObject());
                jobCount++;
                groupCounter++;
            }
            channelPartnerCount++;
            jobCount = 0;
        }
        quartzScheduler.setTriggers(triggers.stream().toArray(Trigger[]::new));

        return quartzScheduler;
    }

    @Bean
    @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public JobDetailFactoryBean jobBean() {
        JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
        jobDetailFactoryBean.setJobClass(findJobByConfig(
                channelPartnerProperties.getChannelPartners().get(channelPartnerCount).getJobConfigs().get(jobCount)));
        jobDetailFactoryBean.setGroup("mainGroup" + groupCounter);
        jobDetailFactoryBean.setName(channelPartnerProperties.getChannelPartners().get(channelPartnerCount)
                .getJobConfigs().get(jobCount).getName());
        jobDetailFactoryBean.setBeanName(channelPartnerProperties.getChannelPartners().get(channelPartnerCount)
                .getJobConfigs().get(jobCount).getName());
        jobDetailFactoryBean.getJobDataMap().put("channelPartner",
                channelPartnerProperties.getChannelPartners().get(channelPartnerCount));
        return jobDetailFactoryBean;
    }

    @Bean
    @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public CronTriggerFactoryBean jobTrigger() {
        CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
        cronTriggerFactoryBean.setJobDetail(jobBean().getObject());
        cronTriggerFactoryBean.setCronExpression(channelPartnerProperties.getChannelPartners().get(channelPartnerCount)
                .getJobConfigs().get(jobCount).getSchedule());
        cronTriggerFactoryBean.setGroup("mainGroup" + groupCounter);
        cronTriggerFactoryBean.setBeanName(channelPartnerProperties.getChannelPartners().get(channelPartnerCount)
                .getJobConfigs().get(jobCount).getName() + "Trigger" + groupCounter);
        return cronTriggerFactoryBean;
    }

    @Override
    public Class<? extends Job> findJobByConfig(JobConfig jobConfig) {
        if (isAllotmentJob(jobConfig) && isHotelJob(jobConfig)) {
            return HotelAndAllotmentEdfJob.class;
        }
        if (isAllotmentJob(jobConfig)) {
            return AllotmentEdfJob.class;
        }
        if (isHotelJob(jobConfig)) {
            return HotelEdfJob.class;
        }
        return HotelAndAllotmentEdfJob.class;
    }

    private boolean isAllotmentJob(JobConfig jobConfig) {
        return jobConfig.isAllotmentEDF();
    }

    private boolean isHotelJob(JobConfig jobConfig) {
        return jobConfig.isHotelEDF();
    }

All the defined jobs in my yaml configuration gets initialized and executed like they defined.

Its a working solution but a workaround. Maybe we find a better one.

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