手写Spring-第十六章-旋转吧雪月花!用三级缓存解决循环依赖

前言

循环依赖,一直是一个令人头疼的问题。虽然我们一般情况下会尽量避免这种情况的发生,但很多时候它会在无意识的情况下出现。比如隔了好几个bean之后,发现循环起来了。那么什么是循环依赖呢?其实就是A依赖B,但B又依赖A。或者A依赖B,B依赖C,C又依赖A这样,构成一个循环。按照我们之前的实现,是无法解决这种循环的。因为之前的逻辑实际上比较单纯,就是遍历Bean定义,然后逐一实例化,遇到依赖的Bean,那就先去实例化依赖Bean。但这样就会发现,实例依赖Bean的时候,依赖Bean又依赖当前Bean。这样就一直循环下去,最后StackOverFlow。那么Spring是怎么解决这个问题的呢?答案是利用三级缓存。三级缓存分别存放不同阶段的Bean,这样,我们在依赖时,就可以去依赖一个暂时还没有构建完成的Bean,就避免了死循环下去的情况。

工程结构

├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─akitsuki
│  │  │          └─springframework
│  │  │              ├─aop
│  │  │              │  │  AdvisedSupport.java
│  │  │              │  │  Advisor.java
│  │  │              │  │  BeforeAdvice.java
│  │  │              │  │  ClassFilter.java
│  │  │              │  │  MethodBeforeAdvice.java
│  │  │              │  │  MethodMatcher.java
│  │  │              │  │  Pointcut.java
│  │  │              │  │  PointcutAdvisor.java
│  │  │              │  │  TargetSource.java
│  │  │              │  │  
│  │  │              │  ├─aspect
│  │  │              │  │      Aspect.java
│  │  │              │  │      AspectJExpressionPointcut.java
│  │  │              │  │      AspectJExpressionPointcutAdvisor.java
│  │  │              │  │      Before.java
│  │  │              │  │  
│  │  │              │  ├─config
│  │  │              │  │      AopBeanRegistryPostProcessor.java
│  │  │              │  │  
│  │  │              │  └─framework
│  │  │              │      │  AopProxy.java
│  │  │              │      │  Cglib2AopProxy.java
│  │  │              │      │  JdkDynamicAopProxy.java
│  │  │              │      │  ProxyFactory.java
│  │  │              │      │  ReflectiveMethodInvocation.java
│  │  │              │      │  
│  │  │              │      ├─adapter
│  │  │              │      │      MethodBeforeAdviceInterceptor.java
│  │  │              │      │  
│  │  │              │      └─autoproxy
│  │  │              │              DefaultAdvisorAutoProxyCreator.java
│  │  │              │          
│  │  │              ├─beans
│  │  │              │  ├─exception
│  │  │              │  │      BeanException.java
│  │  │              │  │  
│  │  │              │  └─factory
│  │  │              │      │  Aware.java
│  │  │              │      │  BeanClassLoaderAware.java
│  │  │              │      │  BeanFactory.java
│  │  │              │      │  BeanFactoryAware.java
│  │  │              │      │  BeanNameAware.java
│  │  │              │      │  ConfigurableListableBeanFactory.java
│  │  │              │      │  DisposableBean.java
│  │  │              │      │  FactoryBean.java
│  │  │              │      │  HierarchicalBeanFactory.java
│  │  │              │      │  InitializingBean.java
│  │  │              │      │  ListableBeanFactory.java
│  │  │              │      │  ObjectFactory.java
│  │  │              │      │  PropertyPlaceholderConfigurer.java
│  │  │              │      │  
│  │  │              │      ├─annotation
│  │  │              │      │      Autowired.java
│  │  │              │      │      AutowiredAnnotationBeanPostProcessor.java
│  │  │              │      │      Qualifier.java
│  │  │              │      │      Value.java
│  │  │              │      │  
│  │  │              │      ├─config
│  │  │              │      │      AutowireCapableBeanFactory.java
│  │  │              │      │      BeanDefinition.java
│  │  │              │      │      BeanDefinitionRegistryPostProcessor.java
│  │  │              │      │      BeanFactoryPostProcessor.java
│  │  │              │      │      BeanPostProcessor.java
│  │  │              │      │      BeanReference.java
│  │  │              │      │      ConfigurableBeanFactory.java
│  │  │              │      │      DefaultSingletonBeanRegistry.java
│  │  │              │      │      InstantiationAwareBeanPostProcessor.java
│  │  │              │      │      PropertyValue.java
│  │  │              │      │      PropertyValues.java
│  │  │              │      │      SingletonBeanRegistry.java
│  │  │              │      │  
│  │  │              │      ├─support
│  │  │              │      │      AbstractAutowireCapableBeanFactory.java
│  │  │              │      │      AbstractBeanDefinitionReader.java
│  │  │              │      │      AbstractBeanFactory.java
│  │  │              │      │      BeanDefinitionReader.java
│  │  │              │      │      BeanDefinitionRegistry.java
│  │  │              │      │      CglibSubclassingInstantiationStrategy.java
│  │  │              │      │      DefaultListableBeanFactory.java
│  │  │              │      │      DisposableBeanAdapter.java
│  │  │              │      │      FactoryBeanRegistrySupport.java
│  │  │              │      │      InstantiationStrategy.java
│  │  │              │      │      SimpleInstantiationStrategy.java
│  │  │              │      │  
│  │  │              │      └─xml
│  │  │              │              XmlBeanDefinitionReader.java
│  │  │              │          
│  │  │              ├─context
│  │  │              │  │  ApplicationContext.java
│  │  │              │  │  ApplicationContextAware.java
│  │  │              │  │  ApplicationEvent.java
│  │  │              │  │  ApplicationEventPublisher.java
│  │  │              │  │  ApplicationListener.java
│  │  │              │  │  ConfigurableApplicationContext.java
│  │  │              │  │  
│  │  │              │  ├─annotation
│  │  │              │  │      ClassPathBeanDefinitionScanner.java
│  │  │              │  │      ClassPathScanningCandidateComponentProvider.java
│  │  │              │  │      Scope.java
│  │  │              │  │  
│  │  │              │  ├─event
│  │  │              │  │      AbstractApplicationEventMulticaster.java
│  │  │              │  │      ApplicationContextEvent.java
│  │  │              │  │      ApplicationEventMulticaster.java
│  │  │              │  │      ContextClosedEvent.java
│  │  │              │  │      ContextRefreshEvent.java
│  │  │              │  │      SimpleApplicationEventMulticaster.java
│  │  │              │  │  
│  │  │              │  └─support
│  │  │              │          AbstractApplicationContext.java
│  │  │              │          AbstractRefreshableApplicationContext.java
│  │  │              │          AbstractXmlApplicationContext.java
│  │  │              │          ApplicationContextAwareProcessor.java
│  │  │              │          ClasspathXmlApplicationContext.java
│  │  │              │      
│  │  │              ├─core
│  │  │              │  └─io
│  │  │              │          ClasspathResource.java
│  │  │              │          DefaultResourceLoader.java
│  │  │              │          FileSystemResource.java
│  │  │              │          Resource.java
│  │  │              │          ResourceLoader.java
│  │  │              │          UrlResource.java
│  │  │              │      
│  │  │              ├─stereotype
│  │  │              │      Component.java
│  │  │              │  
│  │  │              └─util
│  │  │                      ClassUtils.java
│  │  │                      StringValueResolver.java
│  │  │                  
│  │  └─resources
│  └─test
│      ├─java
│      │  └─com
│      │      └─akitsuki
│      │          └─springframework
│      │              └─test
│      │                  │  ApiTest.java
│      │                  │  
│      │                  └─bean
│      │                          IUserService.java
│      │                          UserDao.java
│      │                          UserService.java
│      │                          UserServiceBeforeAdvice.java
│      │                      
│      └─resources
│              application.yml
│              spring.xml

包装起来,提前暴露

这里我们有一个提前暴露的概念。什么是提前暴露呢?其实就是在Bean还没有完全创建好的时候,就可以让其他Bean来进行引用。那你可能会有一个疑问:我为什么要包装起来?我直接给它一个半成品Bean不好吗?你说的对,但是我们还要考虑一种情况:代理Bean。AOP是需要进行代理的,我们的原始Bean会被代理之后提供出去。这个时候如果我们的半成品Bean被引用了,那么引用的就是原始Bean,而不是代理之后的Bean,很显然,这里的代理功能也会失效。所以我们需要一个包装,来作为三级缓存的Bean。

package com.akitsuki.springframework.beans.factory;

import com.akitsuki.springframework.beans.exception.BeanException;

/**
 * 用于在三级缓存时包装bean,解决循环依赖
 * @author ziling.wang@hand-china.com
 * @date 2022/12/15 16:18
 */
public interface ObjectFactory<T> {

    T getObject() throws BeanException;
}

看到这个结构,很容易会联想到我们之前的FactoryBean。它们确实很类似,就连Spring官方的代码注释里面也提到了这一点。不过,这里的ObjectFactory只是用来包装Bean的,而FactoryBean,却是用来生产Bean的。

设置三级缓存

有了上面的包装,我们就可以着手进行三级缓存的设计了。这三级缓存是按照下面的规则进行的:

  1. 一级缓存:存放已经完全生产好、可以直接使用的单例bean
  2. 二级缓存:存放已经实例化好,但还没有进行属性填充的半成品单例bean
  3. 三级缓存:存放用ObjectFactory进行包装的bean(这时应该还不能叫bean)

其实一级缓存我们一直都有在用,就是我们的单例bean缓存。而现在我们要加上二级和三级缓存。这里我们要去动一下许久没有动过的类:DefaultSingletonBeanRegistry。我们之前的单例bean缓存,现在被称为一级缓存,就在它里面。

package com.akitsuki.springframework.beans.factory.config;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.DisposableBean;
import com.akitsuki.springframework.beans.factory.ObjectFactory;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 默认的单例Bean注册获取实现
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 9:58
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    protected static final Object NULL_OBJECT = new Object();

    /**
     * 一级缓存,存放已经处理完成的bean
     */
    private final Map<String, Object> singletonBeans = new HashMap<>();

    /**
     * 二级缓存,存放半成品bean
     */
    protected final Map<String, Object> earlySingletonObjects = new HashMap<>();

    /**
     * 三级缓存,存放代理对象
     */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();

    private final Map<String, DisposableBean> disposableBeans = new HashMap<>();

    @Override
    public Object getSingleton(String beanName) {
        //先从一级缓存中取
        Object bean = singletonBeans.get(beanName);
        if (null == bean) {
            //从二级缓存中找
            bean = earlySingletonObjects.get(beanName);
            if (null == bean) {
                //从三级缓存中找
                ObjectFactory<?> factory = singletonFactories.get(beanName);
                if (null != factory) {
                    bean = factory.getObject();
                    //将bean从三级缓存中移入二级缓存(移入的是getObject之后的结果),同时移除三级缓存中的数据
                    earlySingletonObjects.put(beanName, bean);
                    singletonFactories.remove(beanName);
                }
            }
        }
        return bean;
    }

    @Override
    public void addSingleton(String beanName, Object singletonBean) {
        //在添加单例bean的时候,放入一级缓存,同时要注意清理二级缓存和三级缓存
        singletonBeans.put(beanName, singletonBean);
        earlySingletonObjects.remove(beanName);
        singletonFactories.remove(beanName);
    }

    /**
     * 添加一个bean进入三级缓存
     * 这里添加进入之后,要移除二级缓存中的数据
     * 因为可能对于这个Bean,生成了多个三级缓存
     * 而在前面的某个三级缓存已经被移入二级缓存
     * 所以就需要移除掉二级缓存,以最新的三级缓存为主
     * @param beanName
     * @param objectFactory
     */
    protected void addSingletonFactory(String beanName, ObjectFactory<?> objectFactory) {
        if (!singletonFactories.containsKey(beanName)) {
            singletonFactories.put(beanName, objectFactory);
            earlySingletonObjects.remove(beanName);
        }
    }

    @Override
    public void registerDisposableBean(String beanName, DisposableBean bean) {
        disposableBeans.put(beanName, bean);
    }

    /**
     * 销毁单例对象
     */
    public void destroySingletons() {
        String[] keys = disposableBeans.keySet().toArray(new String[0]);
        for (String key : keys) {
            DisposableBean bean = disposableBeans.remove(key);
            try {
                bean.destroy();
            } catch (Exception e) {
                throw new BeanException("exception on destroy bean " + key, e);
            }
        }
    }

}

我们来看一下变化。首先自然是类的属性里面,增加了二级缓存和三级缓存,都是 HashMap。二级缓存和一级缓存类似,都是存储 Object类型对象,也就是我们实际的Bean。三级缓存则存储 ObjectFactory类型的对象,它不是真正的Bean,我们需要调用 getObject方法,来获取需要的Bean。

接下来是 getSingleton方法,这个是我们之前用来获取单例对象的方法,现在需要进行三级缓存的判断。过程其实就是一级一级缓存去找,找到了就返回。这里需要注意有些不同的是三级缓存的操作,三级缓存在取出bean之后,要将bean移入二级缓存,同时删除三级缓存的内容。为什么要这样操作呢?因为三级缓存实际存储的是一个Factory,工厂。这也就意味着,我们从三级缓存中每次 getObject,实际上生产的都是一个新的对象,这不是我们想要的。既然是单例bean,那么就应该只有一份。

最后是 addSingletonaddSingletonFactory方法,分别是添加单例bean和添加三级缓存的单例bean。在添加单例bean的时候,增加了移除二级和三级缓存的操作,很好理解,因为这个时候bean已经完全生产完成了,所以就不需要二级和三级缓存了。而添加三级缓存的时候,则需要移除二级缓存。

老大难:AOP

AOP一直是我们的老大难问题,因为它和一般的Bean不一样,它是需要进行代理增强的。一旦从代理这块走一圈之后,事情就会变得复杂起来。这也是为什么要设计三级缓存的原因。如果没有AOP这个代理的操作,其实只需要二级缓存就够了。就是因为有了代理,才需要第三级的缓存。

这里我们要根据三级缓存,对AOP的过程进行一些改造。首先是 InstantiationAwareBeanPostProcessor的改造,这里要加一个方法:getEarlyBeanReference。这个方法的作用是用来给前面的ObjectFactory生产bean的。对于一般的bean来说,这里直接返回bean本身就可以了。但是对于AOP的bean来说,则需要进行包装。

    default Object getEarlyBeanReference(Object bean, String beanName) {
        return bean;
    }

可以看到,这个接口用了默认方法,如果子类不进行复写,则会默认返回bean本身。下面我们要做的,就是修改需要复写这个方法的子类:DefaultAdvisorAutoProxyCreator

package com.akitsuki.springframework.aop.framework.autoproxy;

import com.akitsuki.springframework.aop.*;
import com.akitsuki.springframework.aop.aspect.AspectJExpressionPointcutAdvisor;
import com.akitsuki.springframework.aop.framework.ProxyFactory;
import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.BeanFactory;
import com.akitsuki.springframework.beans.factory.BeanFactoryAware;
import com.akitsuki.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import com.akitsuki.springframework.beans.factory.config.PropertyValues;
import com.akitsuki.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/6 15:31
 */
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private DefaultListableBeanFactory beanFactory;

    private final Set<Object> earlyProxyReference = Collections.synchronizedSet(new HashSet<>());

    private boolean isInfrastructureClass(Class<?> beanClass) {
        return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeanException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeanException {
        if (!earlyProxyReference.contains(beanName)) {
            return wrapIfNecessary(bean);
        }
        return bean;
    }

    protected Object wrapIfNecessary(Object bean) {
        if (isInfrastructureClass(bean.getClass())) {
            return bean;
        }
        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();

        for (AspectJExpressionPointcutAdvisor advisor : advisors) {
            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            if (!classFilter.matches(bean.getClass())) {
                continue;
            }
            AdvisedSupport advisedSupport = new AdvisedSupport();
            TargetSource targetSource = new TargetSource(bean);

            advisedSupport.setTargetSource(targetSource);
            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setProxyTargetClass(true);

            return new ProxyFactory(advisedSupport).getProxy();
        }
        return bean;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeanException {
        return null;
    }

    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeanException {
        return pvs;
    }

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        earlyProxyReference.add(beanName);
        return wrapIfNecessary(bean);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeanException {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeanException {
        return true;
    }
}

我们来逐步分析。首先可以看到,增加了一个属性:earlyProxyReference。它的作用我们下面再解释。然后可以看到,之前写在 postProcessAfterInitialization中的逻辑,被移到了一个独立的方法:wrapIfNecessary中。这里在调用之前,可以看到,会先判断 earlyProxyReference中是否包含beanName。可以推断出,earlyProxyReference是用来存储beanName的,它的作用就是防止bean被多次包装。在下面的 getEarlyBeanReference方法中,我们可以看到,会在这里将beanName添加到 earlyProxyReference中。

那么这里的 getEarlyBeanReference方法会在什么时候被调用呢?我们下一节来介绍。

逃不了的生命周期

终究还是回到了生命周期这里。别的地方再怎么花里胡哨,终究还是要有一个启动点。那么这个启动点,就是创建bean的时候,我们几乎每次都要改的:AbstractAutowireCapableBeanFactory

@Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeanException {
        Object bean;
        try {
            bean = resolveBeforeInstantiation(beanName, beanDefinition);
            if (null != bean) {
                return bean;
            }
            //实例化bean
            bean = createBeanInstance(beanDefinition, beanName, args);
            //处理循环依赖
            if (beanDefinition.isSingleton()) {
                Object finalBean = bean;
                addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean));
            }
            boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(beanName, bean);
            if (!continueWithPropertyPopulation) {
                return bean;
            }
            //设置bean属性之前,允许beanPostProcessor修改属性值
            applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);
            //设置bean属性
            applyPropertyValues(beanName, beanDefinition, bean);
            //初始化bean,执行beanPostProcessor的前置和后置方法
            bean = initializeBean(beanName, bean, beanDefinition);
        } catch (Exception e) {
            throw new BeanException("创建bean失败", e);
        }
        //注册实现了DisposableBean接口的对象
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
        Object exposedObject = bean;
        if (beanDefinition.isSingleton()) {
            exposedObject = getSingleton(beanName);
            //创建好的单例bean,放入缓存
            addSingleton(beanName, exposedObject);
        }
        return exposedObject;
    }




    protected Object getEarlyBeanReference(String beanName, BeanDefinition beanDefinition, Object bean) {
        Object exposedBean = bean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            if (processor instanceof InstantiationAwareBeanPostProcessor) {
                exposedBean =  ((InstantiationAwareBeanPostProcessor) processor).getEarlyBeanReference(exposedBean, beanName);
                if (null == exposedBean) {
                    return null;
                }
            }
        }
        return exposedBean;
    }



    /**
     * 对于返回false的bean,不再执行后续设置bean属性的操作
     * @param beanName
     * @param bean
     * @return
     */
    protected boolean applyBeanPostProcessorsAfterInstantiation(String beanName, Object bean) {
        boolean continueWithPropertyPopulation = true;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            if (processor instanceof InstantiationAwareBeanPostProcessor) {
                if (!((InstantiationAwareBeanPostProcessor) processor).postProcessAfterInstantiation(bean, beanName)) {
                    continueWithPropertyPopulation = false;
                    break;
                }
            }
        }
        return continueWithPropertyPopulation;
    }

我们首先看中间的方法 getEarlyBeanReference,可以看到,这个方法和其他后置处理器的处理类似,都是遍历然后调用方法。这里可以看到前面提到的 getEarlyBeanReference 方法,就是在这里被调用的。那么问题又来了,这个方法在什么时候调用呢?那就得看看上面的 createBean了。

我们看处理循环依赖那一段,在这里,我们将实例化好的初始bean,用 ObjectFactory包装好,调用 addSingletonFactory方法(还记得在哪里声明的这个方法吗?在单例bean注册类那里),添加到三级缓存中。这里有个点要注意,这里是采用lambda表达式+匿名内部类的方式进行的,可以认为这里的写法等效于新建了一个 ObjectFactory的对象,而对象的 getObject方法,内容就是调用 getEarlyBeanReference方法。这样可以设想,当一个bean是普通bean的时候,调用 getEarlyBeanReference,会走默认的直接返回原始bean。而一个bean被AOP代理之后,就会走到 DefaultAdvisorAutoProxyCreator中,进行一系列包装,最终返回一个包装类。这样就解决了AOP的循环依赖问题。

最后的地方是针对之前的特殊创建bean方法的一个修改,一般这里都会返回true。当一个bean要走特殊创建流程的时候,这里就会返回false。然后在createBean中调用的时候,就会直接返回bean,不走下面的设置属性等流程。

最后还有一个地方需要注意一下,被cglib代理过的对象,是不能够被再次代理的。所以我们之前创建bean的时候,都是用cglib方式去创建,如果这里再去用cglib创建代理bean,就会报错。那么我们就需要将之前创建bean的方式修改为使用JDK普通方式进行创建,在 DefaultListableBeanFactory中,将策略调整为 SimpleInstantiationStrategy

    public DefaultListableBeanFactory() {
        this(SimpleInstantiationStrategy.class);
    }

    public DefaultListableBeanFactory(Class<? extends InstantiationStrategy> clazz) {
        super(clazz);
    }

测试!

终于到了测试环节。这次的测试,其实代码方面基本不需要动,只需要把之前的测试类拷贝过来就可以了,我们需要IUserService接口、UserDao、UserService、UserServiceBeforeAdvice,还有主要测试类ApiTest。内容基本不用变动。

但是这里我们为了测试循环依赖,特地让UserDao再去依赖一下UserService。

package com.akitsuki.springframework.test.bean;

import com.akitsuki.springframework.beans.factory.DisposableBean;
import com.akitsuki.springframework.beans.factory.InitializingBean;
import com.akitsuki.springframework.beans.factory.annotation.Autowired;
import com.akitsuki.springframework.context.annotation.Scope;
import com.akitsuki.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/8 14:42
 */
@Component
public class UserDao implements InitializingBean, DisposableBean {

    private static final Map<Long, String> userMap = new HashMap<>();

    @Autowired
    private IUserService userService;

    public String queryUserName(Long id) {
        return userMap.get(id);
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("执行UserDao的destroyMethod");
        userMap.clear();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("执行UserDao的initMethod");
        userMap.put(1L, "akitsuki");
        userMap.put(2L, "toyosaki");
        userMap.put(3L, "kugimiya");
        userMap.put(4L, "hanazawa");
        userMap.put(5L, "momonogi");
    }
}

测试结果:

userService的afterPropertiesSet执行了
执行UserDao的initMethod
方法执行了:public void com.akitsuki.springframework.test.bean.UserService.queryUserInfo(java.lang.Long)
dummyString:kamisama
dummyInt:114514
用户名:akitsuki
执行UserDao的destroyMethod
userService的destroy执行了

Process finished with exit code 0

可以看到,正常工作,各种初始化方法和销毁方法都正常工作,切面也正常工作,证明我们的循环依赖成功进行了。

那么在最后,我们再来梳理一下Spring解决循环依赖的步骤。就用这里的UserService和UserDao来举例子,假设Spring先构建UserDao。

  1. 创建UserDao,这时放入三级缓存;
  2. 解决UserDao的属性注入,在这一步,UserDao已经从三级缓存放入了二级缓存,发现UserDao依赖UserService;
  3. 创建UserService,放入三级缓存;
  4. 解决UserService的属性注入,在这一步从三级缓存取出对象的时候,完成了UserService的AOP代理包装步骤。放入二级缓存,发现UserService依赖UserDao;
  5. 从二级缓存中找到UserDao,让UserService成功依赖,UserService创建完成,放入一级缓存;
  6. 回到UserDao的属性注入解决过程,这时候拿到了创建好的UserService,成功依赖,UserDao创建完成,放入一级缓存

以上就是解决循环依赖的全部过程。说实话,我在调试这段程序的时候,花了很长时间,因为一个地方写的有些问题,来回调试,AOP总是无法成功代理,使用的总是原始对象。最后经过多次的断点调试,终于是找到了问题所在。好在经过这些断点调试,也让我搞明白了循环依赖的解决过程。所以如果觉得这里还是有些不清楚,不妨把程序跑起来,断点跟踪一遍,说不定会帮助理解。

相关源码可以参考我的gitee:https://gitee.com/akitsuki-kouzou/mini-spring,这里对应的代码是mini-spring-16

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值