通过 Spring 4 配置时,Quartz Scheduler 不会触发作业
Quartz Scheduler not triggering the job when configured via Spring 4
我一直在尝试设置一个小程序,该程序同时使用 Spring 和 Quartz 来安排任务。我没有运气就遵循了其他一些类似的答案。
目前我认为我已经全部配置正确,我没有看到更多异常,但我的工作看起来没有开始。
在 Spring 生成的 log.out 中,我在末尾看到以下消息:
2015-06-04T15:46:57.928 DEBUG
[org.springframework.core.env.PropertySourcesPropertyResolver]
Searching for key 'spring.liveBeansView.mbeanDomain' in
[systemProperties] 2015-06-04T15:46:57.929 DEBUG
[org.springframework.core.env.PropertySourcesPropertyResolver]
Searching for key 'spring.liveBeansView.mbeanDomain' in
[systemEnvironment] 2015-06-04T15:46:57.929 DEBUG
[org.springframework.core.env.PropertySourcesPropertyResolver] Could
not find key 'spring.liveBeansView.mbeanDomain' in any property
source. Returning [null]
我会告诉你我的代码...
这是我启动调度程序的class:
public class JobRunner {
public static void main(String[] args) throws SchedulerException {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(WhatsTheTimeConfiguration.class);
AutowiringSpringBeanJobFactory autowiringSpringBeanJobFactory = new AutowiringSpringBeanJobFactory();
autowiringSpringBeanJobFactory.setApplicationContext(applicationContext);
SpringBeanJobFactory springBeanJobFactory = new SpringBeanJobFactory();
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setTriggers(trigger());
schedulerFactoryBean.setJobFactory(springBeanJobFactory);
schedulerFactoryBean.start();
}
private static SimpleTrigger trigger() {
return newTrigger()
.withIdentity("whatsTheTimeJobTrigger", "jobsGroup1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build();
}
}
我想提一下,如果我使用 schedulerFactoryBean.getScheduler().start() 方法,它会在调度程序上抛出一个空指针异常,这就是我在工厂上调用 start() 的原因。
class AutowiringSpringBeanJobFactory 是从 Whosebug 中的另一个答案复制粘贴而来的。我决定这样做,因为我发现的所有其他答案都只是通过 xml 完成的配置,我不想使用 xml.
public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
这是代表我要触发的作业的 class:
@Component
public class WhatsTheTimeManager extends QuartzJobBean {
@Autowired
private WhatsTheTime usecase;
@Autowired
private LocationRetriever locationDataProvider;
public WhatsTheTimeManager() {
}
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
usecase.tellMeWhatsTheTimeIn(locationDataProvider.allLocations());
}
public void setUsecase(WhatsTheTime usecase) {
this.usecase = usecase;
}
public void setLocationDataProvider(LocationRetriever locationDataProvider) {
this.locationDataProvider = locationDataProvider;
}
}
我的Spring配置是做组件扫描,很简单:
@Configuration
@ComponentScan(basePackages = "com.springpractice")
public class WhatsTheTimeConfiguration {
}
从现在开始,我所拥有的只是一些接口、组件和域对象,但我也会粘贴它们,以防万一我忘记了什么:
public interface LocationRetriever {
List<String> allLocations();
}
public interface TimeOutputRenderer {
TimeReport renderReport(String timeInLocation, String location);
}
public interface TimeRetriever {
String timeFor(String location);
}
@Component
public class LocationRetrieverDataProvider implements LocationRetriever{
public LocationRetrieverDataProvider() {
}
@Override
public List<String> allLocations() {
return asList("Europe/London", "Europe/Madrid", "Europe/Moscow", "Asia/Tokyo", "Australia/Melbourne", "America/New_York");
}
}
@Component
public class TimeOutputRendererDataProvider implements TimeOutputRenderer {
public TimeOutputRendererDataProvider() {
}
@Override
public TimeReport renderReport(String location, String time) {
System.out.println(location + " time is " + time);
return new TimeReport(location, time);
}
}
@Component
public class TimeRetrieverDataProvider implements TimeRetriever {
public TimeRetrieverDataProvider() {
}
@Override
public String timeFor(String location) {
SimpleDateFormat timeInLocation = new SimpleDateFormat("dd-M-yyyy hh:mm:ss a");
timeInLocation.setTimeZone(TimeZone.getTimeZone(location));
return timeInLocation.format(new Date());
}
}
最后一个细节,您可能会感兴趣。
我在我的库中使用的版本如下:
石英 2.2.1
spring 4.1.6.RELEASE
当我 运行 应用程序时,我希望每秒打印这些国家/地区的时间,但它并没有发生。
如果你想克隆代码并自己尝试看看,你可以在这个 git 存储库中找到它(如果你愿意,请随意分叉):https://github.com/SFRJ/cleanarchitecture
您代码中的主要错误是您没有让 Spring 为您处理日程安排。
虽然您可以在代码中像使用任何其他代码一样使用 Quartz,但与 Spring 集成的想法是告诉 Spring 您想要完成的工作并让 Spring 辛苦你了
为了允许Spring到运行 Quartz 调度,你需要将Job、JobDetail 和Trigger 声明为Bean。
Spring 仅处理通过 Spring 生命周期创建的 Bean(即使用注释或 XML),但如果对象是在代码中使用 new
语句。
需要从 JobRunner.java
中删除以下代码:
SpringBeanJobFactory springBeanJobFactory = new SpringBeanJobFactory();
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setTriggers(trigger());
schedulerFactoryBean.setJobFactory(springBeanJobFactory);
schedulerFactoryBean.start();
...
private static SimpleTrigger trigger() {
return newTrigger()
.withIdentity("whatsTheTimeJobTrigger", "jobsGroup1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build();
}
该代码必须重新写入 WhatsTheTimeConfiguration.java
,现在是这样的:
@Configuration
@ComponentScan(basePackages = "com.djordje.cleanarchitecture")
public class WhatsTheTimeConfiguration {
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setTriggers(trigger());
schedulerFactoryBean.setJobDetails(jobDetail());
schedulerFactoryBean.setJobFactory(springBeanJobFactory());
return schedulerFactoryBean;
}
@Bean
public SpringBeanJobFactory springBeanJobFactory() {
return new AutowiringSpringBeanJobFactory();
}
@Bean
public JobDetail jobDetail() {
JobDetailImpl jobDetail = new JobDetailImpl();
jobDetail.setKey(new JobKey("WhatsTheTime"));
jobDetail.setJobClass(WhatsTheTimeManager.class);
jobDetail.setDurability(true);
return jobDetail;
}
@Bean
public SimpleTrigger trigger() {
return newTrigger()
.forJob(jobDetail())
.withIdentity("whatsTheTimeJobTrigger", "jobsGroup1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build();
}
}
SchedulerFactoryBean
现在是一个Bean,将由Spring处理和初始化,SimpleTrigger
和AutowiringSpringBeanJobFactory
也是如此。
我添加了缺少的 JobDetail
class 并添加了必要的接线到 SimpleTrigger
和 SchedulerFactoryBean
。他们都需要知道 JobDetail
,这是唯一知道哪个 class 是需要触发的工作 class 的地方。
我一直在尝试设置一个小程序,该程序同时使用 Spring 和 Quartz 来安排任务。我没有运气就遵循了其他一些类似的答案。 目前我认为我已经全部配置正确,我没有看到更多异常,但我的工作看起来没有开始。
在 Spring 生成的 log.out 中,我在末尾看到以下消息:
2015-06-04T15:46:57.928 DEBUG [org.springframework.core.env.PropertySourcesPropertyResolver] Searching for key 'spring.liveBeansView.mbeanDomain' in [systemProperties] 2015-06-04T15:46:57.929 DEBUG [org.springframework.core.env.PropertySourcesPropertyResolver] Searching for key 'spring.liveBeansView.mbeanDomain' in [systemEnvironment] 2015-06-04T15:46:57.929 DEBUG [org.springframework.core.env.PropertySourcesPropertyResolver] Could not find key 'spring.liveBeansView.mbeanDomain' in any property source. Returning [null]
我会告诉你我的代码...
这是我启动调度程序的class:
public class JobRunner {
public static void main(String[] args) throws SchedulerException {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(WhatsTheTimeConfiguration.class);
AutowiringSpringBeanJobFactory autowiringSpringBeanJobFactory = new AutowiringSpringBeanJobFactory();
autowiringSpringBeanJobFactory.setApplicationContext(applicationContext);
SpringBeanJobFactory springBeanJobFactory = new SpringBeanJobFactory();
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setTriggers(trigger());
schedulerFactoryBean.setJobFactory(springBeanJobFactory);
schedulerFactoryBean.start();
}
private static SimpleTrigger trigger() {
return newTrigger()
.withIdentity("whatsTheTimeJobTrigger", "jobsGroup1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build();
}
}
我想提一下,如果我使用 schedulerFactoryBean.getScheduler().start() 方法,它会在调度程序上抛出一个空指针异常,这就是我在工厂上调用 start() 的原因。
class AutowiringSpringBeanJobFactory 是从 Whosebug 中的另一个答案复制粘贴而来的。我决定这样做,因为我发现的所有其他答案都只是通过 xml 完成的配置,我不想使用 xml.
public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
这是代表我要触发的作业的 class:
@Component
public class WhatsTheTimeManager extends QuartzJobBean {
@Autowired
private WhatsTheTime usecase;
@Autowired
private LocationRetriever locationDataProvider;
public WhatsTheTimeManager() {
}
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
usecase.tellMeWhatsTheTimeIn(locationDataProvider.allLocations());
}
public void setUsecase(WhatsTheTime usecase) {
this.usecase = usecase;
}
public void setLocationDataProvider(LocationRetriever locationDataProvider) {
this.locationDataProvider = locationDataProvider;
}
}
我的Spring配置是做组件扫描,很简单:
@Configuration
@ComponentScan(basePackages = "com.springpractice")
public class WhatsTheTimeConfiguration {
}
从现在开始,我所拥有的只是一些接口、组件和域对象,但我也会粘贴它们,以防万一我忘记了什么:
public interface LocationRetriever {
List<String> allLocations();
}
public interface TimeOutputRenderer {
TimeReport renderReport(String timeInLocation, String location);
}
public interface TimeRetriever {
String timeFor(String location);
}
@Component
public class LocationRetrieverDataProvider implements LocationRetriever{
public LocationRetrieverDataProvider() {
}
@Override
public List<String> allLocations() {
return asList("Europe/London", "Europe/Madrid", "Europe/Moscow", "Asia/Tokyo", "Australia/Melbourne", "America/New_York");
}
}
@Component
public class TimeOutputRendererDataProvider implements TimeOutputRenderer {
public TimeOutputRendererDataProvider() {
}
@Override
public TimeReport renderReport(String location, String time) {
System.out.println(location + " time is " + time);
return new TimeReport(location, time);
}
}
@Component
public class TimeRetrieverDataProvider implements TimeRetriever {
public TimeRetrieverDataProvider() {
}
@Override
public String timeFor(String location) {
SimpleDateFormat timeInLocation = new SimpleDateFormat("dd-M-yyyy hh:mm:ss a");
timeInLocation.setTimeZone(TimeZone.getTimeZone(location));
return timeInLocation.format(new Date());
}
}
最后一个细节,您可能会感兴趣。 我在我的库中使用的版本如下:
石英 2.2.1
spring 4.1.6.RELEASE
当我 运行 应用程序时,我希望每秒打印这些国家/地区的时间,但它并没有发生。
如果你想克隆代码并自己尝试看看,你可以在这个 git 存储库中找到它(如果你愿意,请随意分叉):https://github.com/SFRJ/cleanarchitecture
您代码中的主要错误是您没有让 Spring 为您处理日程安排。
虽然您可以在代码中像使用任何其他代码一样使用 Quartz,但与 Spring 集成的想法是告诉 Spring 您想要完成的工作并让 Spring 辛苦你了
为了允许Spring到运行 Quartz 调度,你需要将Job、JobDetail 和Trigger 声明为Bean。
Spring 仅处理通过 Spring 生命周期创建的 Bean(即使用注释或 XML),但如果对象是在代码中使用 new
语句。
需要从 JobRunner.java
中删除以下代码:
SpringBeanJobFactory springBeanJobFactory = new SpringBeanJobFactory();
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setTriggers(trigger());
schedulerFactoryBean.setJobFactory(springBeanJobFactory);
schedulerFactoryBean.start();
...
private static SimpleTrigger trigger() {
return newTrigger()
.withIdentity("whatsTheTimeJobTrigger", "jobsGroup1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build();
}
该代码必须重新写入 WhatsTheTimeConfiguration.java
,现在是这样的:
@Configuration
@ComponentScan(basePackages = "com.djordje.cleanarchitecture")
public class WhatsTheTimeConfiguration {
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setTriggers(trigger());
schedulerFactoryBean.setJobDetails(jobDetail());
schedulerFactoryBean.setJobFactory(springBeanJobFactory());
return schedulerFactoryBean;
}
@Bean
public SpringBeanJobFactory springBeanJobFactory() {
return new AutowiringSpringBeanJobFactory();
}
@Bean
public JobDetail jobDetail() {
JobDetailImpl jobDetail = new JobDetailImpl();
jobDetail.setKey(new JobKey("WhatsTheTime"));
jobDetail.setJobClass(WhatsTheTimeManager.class);
jobDetail.setDurability(true);
return jobDetail;
}
@Bean
public SimpleTrigger trigger() {
return newTrigger()
.forJob(jobDetail())
.withIdentity("whatsTheTimeJobTrigger", "jobsGroup1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build();
}
}
SchedulerFactoryBean
现在是一个Bean,将由Spring处理和初始化,SimpleTrigger
和AutowiringSpringBeanJobFactory
也是如此。
我添加了缺少的 JobDetail
class 并添加了必要的接线到 SimpleTrigger
和 SchedulerFactoryBean
。他们都需要知道 JobDetail
,这是唯一知道哪个 class 是需要触发的工作 class 的地方。