美文网首页
循环依赖对 @Scheduled 注解的影响

循环依赖对 @Scheduled 注解的影响

作者: 蓝笔头 | 来源:发表于2019-12-21 12:20 被阅读0次

1. 先看例子

有两个类 AB 相互依赖,且都有被 @Scheduled 注解的方法。

@Slf4j
@Component
public class A {
    @Autowired
    private B b;

    @Scheduled(fixedDelay = 1000)
    public void testA() {
      log.info("testA execute. b:{}", b);
    }
}

@Slf4j
@Component
public class B {
    @Autowired
    private A a;

    @Scheduled(fixedDelay = 1000)
    public void testB() {
        log.info("testB execute. a:{}", a);
    }
}

定义一个切面 ScheduleDisableAspect 用来阻止 @Scheduled 方法的执行。

@Configuration
@Aspect
public class ScheduleDisableAspect {

    @Around("@annotation(org.springframework.scheduling.annotation.Scheduled)")
    public Object beforePointCut(ProceedingJoinPoint pjp) throws Throwable {
        return null;
    }
}

启动类 SpringDemoApplication 代码如下所示。

@EnableScheduling
@SpringBootApplication
public class SpringDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringDemoApplication.class, args);
    }
}

运行启动类,结果如下图所示。

图 1:类 A 中的 @Scheduled 方法 testA 没有被拦截

2. 原理分析

2.1 Spring 容器中 bean 的初始化

我们从 SpringApplication.run() 开始分析,关注其 refreshContext() 方法,并定位到 DefaultListableBeanFactorypreInstantiateSingletons() 方法。

SpringApplication.run(SpringDemoApplication.class, args);
   refreshContext(context);
   ((AbstractApplicationContext) applicationContext).refresh()
       finishBeanFactoryInitialization(beanFactory);
           beanFactory.preInstantiateSingletons();      

简单看一下 preInstantiateSingletons 方法内部实现。

// 忽略暂时不用关注的代码
public void preInstantiateSingletons() throws BeansException {
        // Iterate over a copy to allow for init methods which in turn register new bean definitions.
        // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
        List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

        // Trigger initialization of all non-lazy singleton beans...
        for (String beanName : beanNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                getBean(beanName);
            }
        }
    }

继续看 getBean 方法,getBean 方法是 AbstractBeanFactory 类中的方法。

图 2:DefaultListableBeanFactory 祖先类类图.png
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

// 忽略不用关注的代码
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
        @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

    final String beanName = transformedBeanName(name);
    Object bean;

    // 从 Spring 容器中获取单例对象
    // 如果 A 依赖 B,且 B 依赖 A。假设先实例化 A,那么在 A 中注入 B 的时候,B 会获得 A bean提前暴露的引用
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    else {
        try {
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            
            // 先初始化当前 bean dependsOn 的其他 beans
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    getBean(dep);
                }
            }

            // Create bean instance.
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, () -> {
                    return createBean(beanName, mbd, args);
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }
    return (T) bean;
}

2.2 DefaultSingletonBeanRegistry 中的 getSingleton() 方法

从图 2 可以知道,DefaultSingletonBeanRegistryAbstractBeanFactory 的祖先类。

getSingleton() 方法有两类。

  • 一种是创建 bean 时用到的,有 ObjectFactory 参数。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            try {
                // 调用 objectFactory 的 getObject 方法
                // 即上文提到的 createBean(beanName, mbd, args);
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            if (newSingleton) {
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        // 添加 bean 到 singletonObjects 缓存
        this.singletonObjects.put(beanName, singletonObject);
        // 删除 singletonFactories 缓存
        this.singletonFactories.remove(beanName);
        // 删除 earlySingletonObjects 缓存
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}
  • 一种获取 bean 时用到的,没有 ObjectFactory 参数。
public Object getSingleton(String beanName) {
    return getSingleton(beanName, true);
}

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 1. 从 singletonObjects 获取bean实例
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 2. 从 earlySingletonObjects 获取 bean 实例
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 3. 从 singletonFactories 获取 ObjectFactory
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 3. 从 ObjectFactory 获取 bean 实例
                    // 重点:下文中的 AbstractAutowireCapableBeanFactory 的 getEarlyBeanReference 方法就是在这里调用的
                    singletonObject = singletonFactory.getObject();
                    // 添加 bean 实例到 earlySingletonObjects 缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    // 删除对应的 singletonFactories 缓存
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

addSingletonFactory 方法,在下文中的 createBean 方法中会用到。

// 添加 ObjectFactory 到 singletonFactories 缓存
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

2.3 AbstractAutowireCapableBeanFactory 中的 createBean 方法

从图 2 可以知道,AbstractAutowireCapableBeanFactoryAbstractBeanFactory 的子类。

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {
    RootBeanDefinition mbdToUse = mbd;
    try {
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        return beanInstance;
    }
}

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
        throws BeanCreationException {

    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    final Object bean = instanceWrapper.getWrappedInstance();

    // Eagerly cache singletons to be able to resolve circular references
    // even when triggered by lifecycle interfaces like BeanFactoryAware.
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
            isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        // 如果可以提前暴露引用,则添加 getEarlyBeanReference 对象工厂到 singletonFactories 缓存
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        // 填充 bean 的字段
        // A 依赖 B,创建 A bean 的时候,会在这里创建 B bean
        populateBean(beanName, mbd, instanceWrapper);
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }

    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            if (exposedObject == bean) {
                // 如果已经提前暴露过 bean 的引用,说明执行过 getEarlyBeanReference 方法。
                // 且在 initializeBean 中没有改变过 bean 的引用,
                // 则把 singletonObjects 将缓存的 bean 修改为之前提前暴露的引用。
                exposedObject = earlySingletonReference;
            }
        }
    }
    // 返回的 exposedObject 会在 getSingleton 方法中通过 addSingleton 添加到 singletonObjects 缓存中
    return exposedObject;
}

2.4 AbstractAutowireCapableBeanFactory 中的 getEarlyBeanReference 方法

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        // 执行 SmartInstantiationAwareBeanPostProcessor 的 getEarlyBeanReference 方法
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

2.5 Spring 容器中的 BeanPostProcessor 集合

图3:初始化 A 时的 BeanPostProcessor 集合 .png

我们仅关注 AnnotationAwareAspectJAutoProxyCreatorScheduledAnnotationBeanPostProcessor 两个 BPP。

2.5.1 AnnotationAwareAspectJAutoProxyCreator

类继承关系图如下所示。

图4:AnnotationAwareAspectJAutoProxyCreator 继承关系.png

关注其祖先 AbstractAutoProxyCreator 类中的 getEarlyBeanReference()postProcessAfterInitialization() 方法。

public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    // 添加到 earlyProxyReferences 缓存,避免重复创建代理对象。
    this.earlyProxyReferences.put(cacheKey, bean);
    // 生成代理对象
    return wrapIfNecessary(bean, beanName, cacheKey);
}

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // 如果 earlyProxyReferences 缓存没有,才生成代理对象
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

可以发现 getEarlyBeanReference() 方法生成了代理对象后,会导致 postProcessAfterInitialization() 方法不会生成代理对象。

2.5.2 ScheduledAnnotationBeanPostProcessor

ScheduledAnnotationBeanPostProcessor 没有实现 SmartInstantiationAwareBeanPostProcessor 接口,故而没有 getEarlyBeanReference() 方法。

postProcessAfterInitialization 方法如下所示。

public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean instanceof AopInfrastructureBean || bean instanceof TaskScheduler ||
            bean instanceof ScheduledExecutorService) {
        // Ignore AOP infrastructure such as scoped proxies.
        return bean;
    }

    Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
    if (!this.nonAnnotatedClasses.contains(targetClass)) {
        // 找到所有有 @Scheduled 注解的 Method
        Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
                (MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> {
                    Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(
                            method, Scheduled.class, Schedules.class);
                    return (!scheduledMethods.isEmpty() ? scheduledMethods : null);
                });
            // 分别调用 processScheduled 从而运行 ScheduledMethodRunnable 任务
            annotatedMethods.forEach((method, scheduledMethods) ->
                    scheduledMethods.forEach(scheduled -> processScheduled(scheduled, method, bean)));
    }
    return bean;
}

2.5.3 AbstractAutowireCapableBeanFactoryapplyBeanPostProcessorsAfterInitialization 方法

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

问题原因

循环引用导致 AnnotationAwareAspectJAutoProxyCreatorgetEarlyBeanReference() 方法生成了代理对象,从而在调用 postProcessAfterInitialization() 方法时不会生成代理对象。

进而导致 ScheduledAnnotationBeanPostProcessor 使用的 bean 实例是没有被代理的对象,所以不会被 ScheduleDisableAspect 切面拦截。

如果 A 依赖 B,且 B 依赖 A,先创建 A 的话,会导致 A 提前暴露 bean 引用,然后在 populateBean() 中会创建 B 从而获取到 A 提前暴露的 bean 引用,这时,A 已经通过 getEarlyBeanReference() 方法生成了代理对象。进而导致 ApostProcessAfterInitialization() 方法执行时不会生成代理对象,所以被 ScheduledMethodRunnable 管理的 A 对象不是代理对象,因此不会被 ScheduleDisableAspect 切面拦截。

结论

A 依赖 B,且 B 依赖 A 的情况下。

  • 先创建 A,则 A 中的 @Scheduled 方法不会被 ScheduleDisableAspect 切面拦截。
  • 先创建 B,则 B 中的 @Scheduled 方法不会被 ScheduleDisableAspect 切面拦截。

问题拓展

通过 @DependsOn 注解可以控制 bean 的初始化顺序。

在类 A 上加上 @DependsOn("b") 注解,会让 B 先初始化。

@Slf4j
@Component
@DependsOn("b")
public class A {
    @Autowired
    private B b;

    @Scheduled(fixedDelay = 1000)
    public void testA() {
      log.info("testA execute. b:{}", b);
    }
}

运行 SpringDemoApplication 可以发现现象与我们推导的结论一致。

image.png

解决问题的办法

  1. 去掉 AB 中的 @Scheduled 注解。
  2. 新增 ScheduledTask 类,用来执行 AB 中的 @Scheduled 任务。

具体代码如下所示:

@Slf4j
@Component
@DependsOn("b")
public class A {
    @Autowired
    private B b;

    public void testA() {
      log.info("testA execute. b:{}", b);
    }
}

@Slf4j
@Component
public class B {
    @Autowired
    private A a;

    public void testB() {
        log.info("testB execute. a:{}", a);
    }
}

@Component
public class ScheduledTask {
    @Autowired
    private A a;
    @Autowired
    private B b;

    @Scheduled(fixedDelay = 1000)
    public void testA() {
        a.testA();
    }

    @Scheduled(fixedDelay = 1000)
    public void testB() {
        b.testB();
    }
}

运行 SpringDemoApplication 可以发现 @Scheduled 都已经被拦截。

相关文章

网友评论

      本文标题:循环依赖对 @Scheduled 注解的影响

      本文链接:https://www.haomeiwen.com/subject/glzwnctx.html