ScheduledAnnotationBeanPostProcessor.java 8.5 KB
Newer Older
1
/*
2
 * Copyright 2002-2012 the original author or authors.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.scheduling.annotation;

import java.lang.reflect.Method;
20

21 22
import java.util.HashMap;
import java.util.Map;
C
Chris Beams 已提交
23
import java.util.concurrent.ScheduledExecutorService;
24 25 26 27

import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
28 29
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
30
import org.springframework.context.ApplicationListener;
31
import org.springframework.context.EmbeddedValueResolverAware;
32 33 34
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
C
Chris Beams 已提交
35
import org.springframework.scheduling.TaskScheduler;
C
Chris Beams 已提交
36
import org.springframework.scheduling.Trigger;
37
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
38
import org.springframework.scheduling.support.ScheduledMethodRunnable;
39 40 41
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
42
import org.springframework.util.StringValueResolver;
43 44

/**
C
Chris Beams 已提交
45
 * Bean post-processor that registers methods annotated with @{@link Scheduled}
46 47
 * to be invoked by a {@link org.springframework.scheduling.TaskScheduler} according
 * to the "fixedRate", "fixedDelay", or "cron" expression provided via the annotation.
48
 *
C
Chris Beams 已提交
49 50 51 52 53 54 55 56 57
 * <p>This post-processor is automatically registered by Spring's
 * {@code <task:annotation-driven>} XML element, and also by the @{@link EnableScheduling}
 * annotation.
 *
 * <p>Auto-detects any {@link SchedulingConfigurer} instances in the container,
 * allowing for customization of the scheduler to be used or for fine-grained control
 * over task registration (e.g. registration of {@link Trigger} tasks.
 * See @{@link EnableScheduling} Javadoc for complete usage details.
 *
58
 * @author Mark Fisher
59
 * @author Juergen Hoeller
C
Chris Beams 已提交
60
 * @author Chris Beams
61 62
 * @since 3.0
 * @see Scheduled
C
Chris Beams 已提交
63
 * @see EnableScheduling
C
Chris Beams 已提交
64
 * @see SchedulingConfigurer
65
 * @see org.springframework.scheduling.TaskScheduler
C
Chris Beams 已提交
66
 * @see org.springframework.scheduling.config.ScheduledTaskRegistrar
67
 */
68 69 70
public class ScheduledAnnotationBeanPostProcessor
		implements BeanPostProcessor, Ordered, EmbeddedValueResolverAware, ApplicationContextAware,
		ApplicationListener<ContextRefreshedEvent>, DisposableBean {
71 72 73

	private Object scheduler;

74 75
	private StringValueResolver embeddedValueResolver;

76 77
	private ApplicationContext applicationContext;

C
Chris Beams 已提交
78
	private ScheduledTaskRegistrar registrar;
79 80 81 82 83 84 85 86 87

	private final Map<Runnable, String> cronTasks = new HashMap<Runnable, String>();

	private final Map<Runnable, Long> fixedDelayTasks = new HashMap<Runnable, Long>();

	private final Map<Runnable, Long> fixedRateTasks = new HashMap<Runnable, Long>();


	/**
88 89 90
	 * Set the {@link org.springframework.scheduling.TaskScheduler} that will invoke
	 * the scheduled methods, or a {@link java.util.concurrent.ScheduledExecutorService}
	 * to be wrapped as a TaskScheduler.
91 92 93 94 95
	 */
	public void setScheduler(Object scheduler) {
		this.scheduler = scheduler;
	}

96 97 98 99
	public void setEmbeddedValueResolver(StringValueResolver resolver) {
		this.embeddedValueResolver = resolver;
	}

100 101 102 103
	public void setApplicationContext(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}

104 105 106 107
	public int getOrder() {
		return LOWEST_PRECEDENCE;
	}

108
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
109 110 111
		return bean;
	}

112
	public Object postProcessAfterInitialization(final Object bean, String beanName) {
113
		final Class<?> targetClass = AopUtils.getTargetClass(bean);
114 115 116 117 118 119 120 121
		ReflectionUtils.doWithMethods(targetClass, new MethodCallback() {
			public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
				Scheduled annotation = AnnotationUtils.getAnnotation(method, Scheduled.class);
				if (annotation != null) {
					Assert.isTrue(void.class.equals(method.getReturnType()),
							"Only void-returning methods may be annotated with @Scheduled.");
					Assert.isTrue(method.getParameterTypes().length == 0,
							"Only no-arg methods may be annotated with @Scheduled.");
122 123 124 125 126
					if (AopUtils.isJdkDynamicProxy(bean)) {
						try {
							// found a @Scheduled method on the target class for this JDK proxy -> is it
							// also present on the proxy itself?
							method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
127 128
						}
						catch (SecurityException ex) {
129
							ReflectionUtils.handleReflectionException(ex);
130 131
						}
						catch (NoSuchMethodException ex) {
132 133 134 135 136 137 138 139
							throw new IllegalStateException(String.format(
									"@Scheduled method '%s' found on bean target class '%s', " +
									"but not found in any interface(s) for bean JDK proxy. Either " +
									"pull the method up to an interface or switch to subclass (CGLIB) " +
									"proxies by setting proxy-target-class/proxyTargetClass " +
									"attribute to 'true'", method.getName(), targetClass.getSimpleName()));
						}
					}
140
					Runnable runnable = new ScheduledMethodRunnable(bean, method);
141
					boolean processedSchedule = false;
142
					String errorMessage = "Exactly one of the 'cron', 'fixedDelay', or 'fixedRate' attributes is required.";
143 144 145
					String cron = annotation.cron();
					if (!"".equals(cron)) {
						processedSchedule = true;
146 147
						if (embeddedValueResolver != null) {
							cron = embeddedValueResolver.resolveStringValue(cron);
148
						}
149 150 151 152 153 154
						cronTasks.put(runnable, cron);
					}
					long fixedDelay = annotation.fixedDelay();
					if (fixedDelay >= 0) {
						Assert.isTrue(!processedSchedule, errorMessage);
						processedSchedule = true;
155
						fixedDelayTasks.put(runnable, fixedDelay);
156 157 158 159 160
					}
					long fixedRate = annotation.fixedRate();
					if (fixedRate >= 0) {
						Assert.isTrue(!processedSchedule, errorMessage);
						processedSchedule = true;
161
						fixedRateTasks.put(runnable, fixedRate);
162 163 164 165 166 167 168 169 170
					}
					Assert.isTrue(processedSchedule, errorMessage);
				}
			}
		});
		return bean;
	}

	public void onApplicationEvent(ContextRefreshedEvent event) {
C
Chris Beams 已提交
171 172 173 174
		if (event.getApplicationContext() != this.applicationContext) {
			return;
		}

175 176
		Map<String, SchedulingConfigurer> configurers =
				this.applicationContext.getBeansOfType(SchedulingConfigurer.class);
C
Chris Beams 已提交
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195

		if (this.cronTasks.isEmpty() && this.fixedDelayTasks.isEmpty() &&
				this.fixedRateTasks.isEmpty() && configurers.isEmpty()) {
			return;
		}

		this.registrar = new ScheduledTaskRegistrar();
		this.registrar.setCronTasks(this.cronTasks);
		this.registrar.setFixedDelayTasks(this.fixedDelayTasks);
		this.registrar.setFixedRateTasks(this.fixedRateTasks);

		if (this.scheduler != null) {
			this.registrar.setScheduler(this.scheduler);
		}

		for (SchedulingConfigurer configurer : configurers.values()) {
			configurer.configureTasks(this.registrar);
		}

196
		if (this.registrar.getScheduler() == null) {
C
Chris Beams 已提交
197 198 199 200 201
			Map<String, ? super Object> schedulers = new HashMap<String, Object>();
			schedulers.putAll(applicationContext.getBeansOfType(TaskScheduler.class));
			schedulers.putAll(applicationContext.getBeansOfType(ScheduledExecutorService.class));
			if (schedulers.size() == 0) {
				// do nothing -> fall back to default scheduler
202 203
			}
			else if (schedulers.size() == 1) {
C
Chris Beams 已提交
204
				this.registrar.setScheduler(schedulers.values().iterator().next());
205 206 207 208 209 210 211 212
			}
			else if (schedulers.size() >= 2){
				throw new IllegalStateException(
						"More than one TaskScheduler and/or ScheduledExecutorService  " +
						"exist within the context. Remove all but one of the beans; or " +
						"implement the SchedulingConfigurer interface and call " +
						"ScheduledTaskRegistrar#setScheduler explicitly within the " +
						"configureTasks() callback. Found the following beans: " + schedulers.keySet());
213
			}
214
		}
C
Chris Beams 已提交
215 216

		this.registrar.afterPropertiesSet();
217 218 219 220 221 222 223 224 225
	}

	public void destroy() throws Exception {
		if (this.registrar != null) {
			this.registrar.destroy();
		}
	}

}