我有一个QuartzJobConfig
课程,我注册了Spring-Quartz-Beans
。
我按照SchedulerFactoryBean
,JobDetailFactoryBean
和CronTriggerFactoryBean
的说明进行操作。
我的工作在应用程序外的yaml
文件中配置。意味着我必须在应用程序启动时动态创建Bean。
我的配置:
channelPartnerConfiguration:
channelPartners:
- code: Job1
jobConfigs:
- schedule: 0 * * ? * MON-FRI
name: Job1 daily
hotel: false
allotment: true
enabled: true
- schedule: 30 * * ? * MON-FRI
name: Job2 weekly
hotel: true
allotment: false
enabled: true
...
我的配置类:
@Configuration
public class QuartzJobConfig implements IJobClass{
@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);
// point 1
List<Trigger> triggers = new ArrayList<>();
for(ChannelPartner ch : channelPartnerProperties.getChannelPartners()){
for(JobConfig jobConfig : ch.getJobConfigs()){
triggers.add(jobTrigger(ch, jobConfig).getObject());
}
}
quartzScheduler.setTriggers(triggers.stream().toArray(Trigger[]::new));
return quartzScheduler;
}
@Bean
public JobDetailFactoryBean jobBean(ChannelPartner ch, JobConfig jobConfig) {
JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
jobDetailFactoryBean.setJobClass(findJobByConfig(jobConfig));
jobDetailFactoryBean.setGroup("mainGroup");
jobDetailFactoryBean.setName(jobConfig.getName());
jobDetailFactoryBean.setBeanName(jobConfig.getName());
jobDetailFactoryBean.getJobDataMap().put("channelPartner", ch);
return jobDetailFactoryBean;
}
@Bean
public CronTriggerFactoryBean jobTrigger(ChannelPartner ch, JobConfig jobConfig) {
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
cronTriggerFactoryBean.setJobDetail(jobBean(ch, jobConfig).getObject());
cronTriggerFactoryBean.setCronExpression(jobConfig.getSchedule());
cronTriggerFactoryBean.setGroup("mainGroup");
return cronTriggerFactoryBean;
}
@Override
public Class<? extends Job> findJobByConfig(JobConfig jobConfig) {
if(isAllotmentJob(jobConfig) && isHotelJob(jobConfig)){
return HotelAndAllotmentJob.class;
}
if(isAllotmentJob(jobConfig)){
return AllotmentJob.class;
}
if(isHotelJob(jobConfig)){
return HotelJob.class;
}
return HotelAndAllotmentJob.class;
}
private boolean isAllotmentJob(JobConfig jobConfig){
return jobConfig.isAllotment();
}
private boolean isHotelJob(JobConfig jobConfig) {
return jobConfig.isHotel();
}
}
我的问题是在迭代(第1点)内创建Bean只做了一次。在第一次迭代之后,它不再进入jobTrigger(ch, jobConfig)
方法。 (如果我是正确的话,因为bean名称或多或少都清楚了)
我在想,因为我使用Spring的Quartz factories
,jobDetailFactoryBean.setBeanName()
方法用于创建更多具有不同名称的bean。
不确定如何解决这个问题。代码正在运行,第一个创建的作业正在执行。但我需要更多的工作。
如何动态创建不同的作业?
修改
我的完整配置类:
@Configuration
@ConfigurationProperties(prefix = "channelPartnerConfiguration", locations = "classpath:customer/channelPartnerConfiguration.yml")
public class ChannelPartnerProperties {
@Autowired
private List<ChannelPartner> channelPartners;
public List<ChannelPartner> getChannelPartners() {
return channelPartners;
}
public void setChannelPartners(List<ChannelPartner> channelPartners) {
this.channelPartners = channelPartners;
}
}
@Configuration
public class ChannelPartner {
private String code;
private String contracts;
private Boolean includeSpecialContracts;
private String touroperatorCode = "EUTO";
@Autowired
private PublishConfig publishConfig;
@Autowired
private BackupConfig backupConfig;
@Autowired
private List<JobConfig> jobConfigs;
//getter/setter
@Configuration
public class JobConfig {
private String schedule;
private boolean hotelEDF;
private boolean allotmentEDF;
private boolean enabled;
private String name;
//getter/setter
添加project to github以更好地理解问题
答案 0 :(得分:2)
您的列表将包含空值的原因是因为您正在调用的getObject方法应返回CronTrigger,该CronTrigger仅在spring启动spring上下文时由spring调用的afterPropertiesSet方法中启动。您可以在CronTriggerFactoryBean上手动调用此方法,这将允许您将其作为私有方法。
// 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;
}
我确定还有很多其他方法可以做到这一点,正如你自己提到的,你做了一个解决方法,如果这不是你想要或不需要我可以检查更多如果我可以找到更好的方法。
答案 1 :(得分:1)
您的jobTrigger()
和jobBean()
方法不是实际bean,而是您使用的工厂方法给出一些输入来构造CronTrigger
和JobDetail
以在您的循环中注册通过调用quartzScheduler
在您的triggers.add(..)
bean中。
从@Bean
和@Scope
方法中删除jobTrigger()
和jobBean()
注释(理想情况下也会降低其可见性(如果不是私有,请将包私有),您应该对去。
答案 2 :(得分:1)
在尝试使用此代码后,我找到了一个有效的解决方案。它只是一个解决方法,但提供了一些提示,以找到正确的 - 而不是解决方法 - 解决方案。
我做了什么:
@Configuration
除@Component
和ChannelPartnerProperties
以外的QuartzJobConfig
课程。@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
添加到我的jobBean()
和jobTrigger()
方法。@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
。channelPartners
,jobConfigs
和一个TriggerGroups名称。@Autowired channelPartnerProperties
获取正确的对象,其中包含yaml
文件的所有条目。 之后我的QuartzJobConfig
课程看起来像这样:
@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();
}
我的yaml
配置中的所有已定义作业都会按照定义进行初始化和执行。
它是一个有效的解决方案,但是一种解决方法。也许我们找到一个更好的。