开始
本文记录使用Spring @Schedule 配置不生效的问题分析.
项目环境
先说下项目环境,项目中使用的spring版本是4.3.8
.
启用Spring schedule.
@Configuration @EnableScheduling public class AppConfig { }
|
Task 类
public class TestJob {
@Scheduled(fixedDelay = 3000) public void task1(){ } }
|
项目启动后任务并没有按照预期执行.
分析
spring通过@Scheduled
配置job,通过@EnableScheduling
启用计划任务调度
@EnableScheduling
源码
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(SchedulingConfiguration.class) @Documented public @interface EnableScheduling {
}
|
该注解通过@Import
导入SchedulingConfiguration
配置
@Configuration @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class SchedulingConfiguration {
@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() { return new ScheduledAnnotationBeanPostProcessor(); } }
|
BeanPostProcessor
是Spring的中的扩展点,可以对bean初始化后做一些增强操作(eg:声明式事务的实现)
ScheduledAnnotationBeanPostProcessor
的关键代码
@Override public Object postProcessAfterInitialization(final Object bean, String beanName) { Class<?> targetClass = AopUtils.getTargetClass(bean); if (!this.nonAnnotatedClasses.contains(targetClass)) { Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass, new MethodIntrospector.MetadataLookup<Set<Scheduled>>() { @Override public Set<Scheduled> inspect(Method method) { Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations( method, Scheduled.class, Schedules.class); return (!scheduledMethods.isEmpty() ? scheduledMethods : null); } }); if (annotatedMethods.isEmpty()) { this.nonAnnotatedClasses.add(targetClass); if (logger.isTraceEnabled()) { logger.trace("No @Scheduled annotations found on bean class: " + bean.getClass()); } } else { for (Map.Entry<Method, Set<Scheduled>> entry : annotatedMethods.entrySet()) { Method method = entry.getKey(); for (Scheduled scheduled : entry.getValue()) { processScheduled(scheduled, method, bean); } } if (logger.isDebugEnabled()) { logger.debug(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName + "': " + annotatedMethods); } } } return bean; }
|
问题就出现在上述代码中
Class<?> targetClass = AopUtils.getTargetClass(bean);
|
我们查看AopUtils#getTargetClass
源码
public static Class<?> getTargetClass(Object candidate) { Assert.notNull(candidate, "Candidate object must not be null"); Class<?> result = null; if (candidate instanceof TargetClassAware) { result = ((TargetClassAware) candidate).getTargetClass(); } if (result == null) { result = (isCglibProxy(candidate) ? candidate.getClass().getSuperclass() : candidate.getClass()); } return result; }
|
正常情况下这里不会有问题,但是当我们的bean
被多重代理后,获取到target并不是真正的目标对象,因此获取到的targetClass并不是实际的class
最终 MethodIntrospector.selectMethods
去获取 带有@Scheduled
的方法自然获取不到,我们的方法也不会加入任务调度.
这个问题在 spring 4.3.14
版本中被修复了
修复后的源码
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
|
相关Issue Combining @Retryable and @Scheduled/@JmsListener doesn’t work [SPR-16196]