Revised scheduling lifecycle integration

ScheduledAnnotationBeanPostProcessor uses getBean(Class) for TaskScheduler/ScheduledExecutorService retrieval, allowing for a scheduler bean to be flagged as primary, and for a TaskScheduler bean to override a ScheduledExecutorService bean.

ContextLifecycleScheduledTaskRegistrar hooks into SmartInitializingSingleton's afterSingletonsInstantiated callback instead of ContextRefreshedEvent, as a natural consequence of SmartInitializingSingleton's introduction in Spring Framework 4.1 GA.
This commit is contained in:
Juergen Hoeller 2014-10-31 17:26:23 +01:00
parent 7d2231541b
commit 65d163e604
2 changed files with 36 additions and 52 deletions

View File

@ -19,7 +19,6 @@ package org.springframework.scheduling.annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -35,6 +34,8 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -87,7 +88,7 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
private StringValueResolver embeddedValueResolver; private StringValueResolver embeddedValueResolver;
private ListableBeanFactory beanFactory; private BeanFactory beanFactory;
private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar(); private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();
@ -121,7 +122,7 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
*/ */
@Override @Override
public void setBeanFactory(BeanFactory beanFactory) { public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = (beanFactory instanceof ListableBeanFactory ? (ListableBeanFactory) beanFactory : null); this.beanFactory = beanFactory;
} }
/** /**
@ -141,8 +142,9 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
this.registrar.setScheduler(this.scheduler); this.registrar.setScheduler(this.scheduler);
} }
if (this.beanFactory != null) { if (this.beanFactory instanceof ListableBeanFactory) {
Map<String, SchedulingConfigurer> configurers = this.beanFactory.getBeansOfType(SchedulingConfigurer.class); Map<String, SchedulingConfigurer> configurers =
((ListableBeanFactory) this.beanFactory).getBeansOfType(SchedulingConfigurer.class);
for (SchedulingConfigurer configurer : configurers.values()) { for (SchedulingConfigurer configurer : configurers.values()) {
configurer.configureTasks(this.registrar); configurer.configureTasks(this.registrar);
} }
@ -150,21 +152,30 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
if (this.registrar.hasTasks() && this.registrar.getScheduler() == null) { if (this.registrar.hasTasks() && this.registrar.getScheduler() == null) {
Assert.state(this.beanFactory != null, "BeanFactory must be set to find scheduler by type"); Assert.state(this.beanFactory != null, "BeanFactory must be set to find scheduler by type");
Map<String, ? super Object> schedulers = new HashMap<String, Object>(); try {
schedulers.putAll(this.beanFactory.getBeansOfType(TaskScheduler.class)); // Search for TaskScheduler bean...
schedulers.putAll(this.beanFactory.getBeansOfType(ScheduledExecutorService.class)); this.registrar.setScheduler(this.beanFactory.getBean(TaskScheduler.class));
if (schedulers.size() == 0) {
// do nothing -> fall back to default scheduler
} }
else if (schedulers.size() == 1) { catch (NoUniqueBeanDefinitionException ex) {
this.registrar.setScheduler(schedulers.values().iterator().next()); throw new IllegalStateException("More than one TaskScheduler exists within the context. " +
"Remove all but one of the beans; or implement the SchedulingConfigurer interface and call " +
"ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback.", ex);
} }
else if (schedulers.size() >= 2){ catch (NoSuchBeanDefinitionException ex) {
throw new IllegalStateException("More than one TaskScheduler and/or ScheduledExecutorService " + logger.debug("Could not find default TaskScheduler bean", ex);
"exist within the context. Remove all but one of the beans; or implement the " + // Search for ScheduledExecutorService bean next...
"SchedulingConfigurer interface and call ScheduledTaskRegistrar#setScheduler " + try {
"explicitly within the configureTasks() callback. Found the following beans: " + this.registrar.setScheduler(this.beanFactory.getBean(ScheduledExecutorService.class));
schedulers.keySet()); }
catch (NoUniqueBeanDefinitionException ex2) {
throw new IllegalStateException("More than one ScheduledExecutorService exists within the context. " +
"Remove all but one of the beans; or implement the SchedulingConfigurer interface and call " +
"ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback.", ex);
}
catch (NoSuchBeanDefinitionException ex2) {
logger.debug("Could not find default ScheduledExecutorService bean", ex);
// Giving up -> falling back to default scheduler within the registrar...
}
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,51 +16,24 @@
package org.springframework.scheduling.config; package org.springframework.scheduling.config;
import org.springframework.context.ApplicationContext; import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
/** /**
* {@link ScheduledTaskRegistrar} subclass that redirects the actual scheduling * {@link ScheduledTaskRegistrar} subclass which redirects the actual scheduling
* of tasks to the {@link ContextRefreshedEvent} callback. Falls back to regular * of tasks to the {@link #afterSingletonsInstantiated()} callback (as of 4.1.2).
* {@code ScheduledTaskRegistrar} behavior when not running within an ApplicationContext.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 3.2.1 * @since 3.2.1
*/ */
public class ContextLifecycleScheduledTaskRegistrar extends ScheduledTaskRegistrar public class ContextLifecycleScheduledTaskRegistrar extends ScheduledTaskRegistrar implements SmartInitializingSingleton {
implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent> {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* If we're running within an ApplicationContext, don't schedule the tasks
* right here; wait for this context's ContextRefreshedEvent instead.
*/
@Override @Override
public void afterPropertiesSet() { public void afterPropertiesSet() {
if (this.applicationContext == null) { // no-op
scheduleTasks();
}
} }
/**
* Actually schedule the tasks at the right time of the context lifecycle,
* if we're running within an ApplicationContext.
*/
@Override @Override
public void onApplicationEvent(ContextRefreshedEvent event) { public void afterSingletonsInstantiated() {
if (event.getApplicationContext() != this.applicationContext) {
return;
}
scheduleTasks(); scheduleTasks();
} }