通过 Spring 4 配置时,Quartz Scheduler 不会触发作业

sfr*_*frj 2 java spring quartz-scheduler

我曾经尝试设置一个小程序,使用 Spring 和 Quartz 来安排任务。我遵循了其他一些类似的答案,但没有运气。目前我认为我已经正确配置了所有配置,我没有看到更多异常,但我的工作看起来还没有开始。

在 Spring 生成的 log.out 中,我在末尾看到以下消息:

2015-06-04T15:46:57.928调试[org.springframework.core.env.PropertySourcesPropertyResolver]在[systemProperties]中搜索键“spring.liveBeansView.mbeanDomain”2015-06-04T15:46:57.929调试[org.springframework。 core.env.PropertySourcesPropertyResolver] 在 [systemEnvironment] 2015-06-04T15:46:57.929 DEBUG [org.springframework.core.env.PropertySourcesPropertyResolver] 中搜索键“spring.liveBeansView.mbeanDomain”,找不到键“spring.liveBeansView”。任何属性源中的 mbeanDomain'。返回[空]

我会告诉你我的代码...

这是我启动调度程序的类:

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();
    }

}
Run Code Online (Sandbox Code Playgroud)

我想提一下,如果我使用 SchedulerFactoryBean.getScheduler().start() 方法,它会在调度程序上引发空指针异常,这就是为什么我在工厂上调用 start() 的原因。

AutowiringSpringBeanJobFactory 类是从 stackoverflow 中的另一个答案复制粘贴的。我决定这样做,因为我发现的所有其他答案只是通过 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是代表我要触发的作业的类:

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我的Spring配置是进行组件扫描,非常简单:

@Configuration
@ComponentScan(basePackages = "com.springpractice")
public class WhatsTheTimeConfiguration {

}
Run Code Online (Sandbox Code Playgroud)

从现在起,我所拥有的一切都只是一些接口、组件和域对象,但我也会粘贴它们,以防万一我忘记了一些东西:

public interface LocationRetriever {
    List<String> allLocations();
}
Run Code Online (Sandbox Code Playgroud)
public interface TimeOutputRenderer {
    TimeReport renderReport(String timeInLocation, String location);
}
Run Code Online (Sandbox Code Playgroud)
public interface TimeRetriever {
    String timeFor(String location);
}
Run Code Online (Sandbox Code Playgroud)
@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");
    }
}
Run Code Online (Sandbox Code Playgroud)
@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);
    }
}
Run Code Online (Sandbox Code Playgroud)
@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());
    }
}
Run Code Online (Sandbox Code Playgroud)

最后一个细节也许很有趣。我在库中使用的版本如下:

石英2.2.1

弹簧 4.1.6.发布

当我运行该应用程序时,我希望每秒打印这些国家/地区的时间,但这并没有发生。

如果你想克隆代码并亲自尝试一下,你可以在这个 git repo 中找到它(如果你愿意,可以随意 fork): https: //github.com/SFRJ/cleanarchitecture

sum*_*ulb 5

您代码中的主要错误是您没有让 Spring 为您处理调度。

虽然您可以像任何其他代码一样在代码中使用 Quartz,但与 Spring 集成的想法是告诉 Spring 您想要完成的工作,并让 Spring 为您完成困难的工作。

为了让Spring能够运行Quartz调度,需要将Job、JobDetail和Trigger声明为Bean。

Spring 仅处理通过 Spring 生命周期(即使用注释或 XML)创建的 Bean,但如果对象是通过语句在代码中创建的,则 Spring 不处理它们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();
}
Run Code Online (Sandbox Code Playgroud)

该代码必须重新写入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();
    }
}
Run Code Online (Sandbox Code Playgroud)

SchedulerFactoryBean现在是一个 Bean,将由 Spring 处理和初始化, 和 也是SimpleTrigger如此AutowiringSpringBeanJobFactory

我添加了缺失的JobDetail类,并将必要的接线添加到SimpleTriggerSchedulerFactoryBean。他们都需要知道JobDetail唯一知道哪个类是需要触发的作业类。