前言

循环依赖,一直是一个令人头疼的问题。虽然我们一般情况下会尽量避免这种情况的发生,但很多时候它会在无意识的情况下出现。比如隔了好几个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<>();@Overridepublic 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;}@Overridepublic 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);}}@Overridepublic 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);}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeanException {return bean;}@Overridepublic 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;}@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeanException {return null;}@Overridepublic PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeanException {return pvs;}@Overridepublic Object getEarlyBeanReference(Object bean, String beanName) {earlyProxyReference.add(beanName);return wrapIfNecessary(bean);}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeanException {this.beanFactory = (DefaultListableBeanFactory) beanFactory;}@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeanException {return true;}
}

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

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

逃不了的生命周期

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

@Overrideprotected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeanException {Object bean;try {bean = resolveBeforeInstantiation(beanName, beanDefinition);if (null != bean) {return bean;}//实例化beanbean = 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<>();@Autowiredprivate IUserService userService;public String queryUserName(Long id) {return userMap.get(id);}@Overridepublic void destroy() throws Exception {System.out.println("执行UserDao的destroyMethod");userMap.clear();}@Overridepublic 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

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

  1. 京东一面:Spring 为何需要三级缓存解决循环依赖,而不是二级缓存?我懵了。。...

    欢迎关注方志朋的博客,回复"666"获面试宝典 来源:cnblogs.com/semi-sub/p/13548479.html 前言 bean生命周期 三级缓存解决循环依赖 总结 ...

  2. 为什么Spring需要三级缓存解决循环依赖,而不是二级缓存?

    来源:https://www.cnblogs.com/semi-sub/p/13548479.html 在使用spring框架的日常开发中,bean之间的循环依赖太频繁了,spring已经帮我们去解决 ...

  3. Spring三级缓存解决循环依赖问题详解

    spring三级缓存解决循环依赖问题详解 前言 这段时间阅读了spring IOC部分的源码.在学习过程中,自己有遇到过很多很问题,在上网查阅资料的时候,发现很难找到一份比较全面的解答.现在自己刚学习 ...

  4. 攀爬Spring珠穆拉玛峰:preInstantiateSingletons方法、三级缓存、循环依赖

    Spring启动流程 1)Spring 三级缓存与循环依赖 前文回顾 preInstantiateSingletons getBean→doGetBean 自动装载与循环依赖 三级缓存 getSing ...

  5. 解鞍卸甲——手写简易版Spring框架(终):使用三级缓存解决循环依赖问题

    什么是三级缓存 按照目前我们实现的 Spring 框架,是可以满足一个基本需求的,但如果你配置了A.B两个Bean对象互相依赖,那么立马会抛出 java.lang.StackOverflowError ...

  6. Spring三级缓存解决循环依赖

    1. 前言 循环依赖:就是N个类循环(嵌套)引用. 通俗的讲就是N个Bean互相引用对方,最终形成闭环.用一副经典的图示可以表示成这样(A.B.C都代表对象,虚线代表引用关系): 其实可以N=1,也就 ...

  7. Spring使用三级缓存解决循环依赖?终于完全弄明白了

    文章阅读前推荐 推荐先去看看源码,源码很短,但是对于我们在脑子里构建一个完整思路很重要.看起来非常简单,只需要双击shift,全局查找文件:AbstractAutowireCapableBeanFac ...

  8. Spring的三级缓存解决循环依赖

    一.什么是Spring三级缓存 第一级缓存:也叫单例池,存放已经经历了完整生命周期的Bean对象. 第二级缓存:存放早期暴露出来的Bean对象,实例化以后,就把对象放到这个Map中.(Bean可能只经 ...

  9. spring无法用三级缓存解决循环依赖的问题分析

    spring无法解决构造器的循环依赖,对上述例子稍微进行改动: @Component("b") public class B {private A a;public B(A a) ...

最新文章

  1. OpenCASCADE:Modeling Algorithms模块之制作原语Making Primitives
  2. Pod详解-生命周期-容器探测
  3. HDU - 3247 Resource Archiver(AC自动机+状压dp+bfs)
  4. WCF中绑定的简单介绍
  5. 网路流程图 TCP/IP
  6. 【Elasticsearch】Elasticsearch 7 : 动态映射 dynamic
  7. 如何化身BAT面试收割机?都是精髓!
  8. ES(Elasticsearch)基本查询总结(含docker安装,python操作)
  9. 精彩案例:一碗牛肉面的思考
  10. snmp扫描工具linux,SugarNMSTool-SugarNMSTool(snmp工具)下载 v2.0官方版--pc6下载站
  11. 【作业锦集】机器人学导论-空间变换及Matlab实现(part-1)
  12. xss php漏洞扫描工具,XSpear:一款强大的XSS漏洞扫描器工具
  13. 如何设计微信公众号的封面图?教你设计自己的专属公众号封面
  14. C语言ctype常用方法
  15. Adobe Illustrator CS6 出现错误报告16
  16. 云服务器哪家好?云服务器评测对比
  17. NC WebService开发参考
  18. mac下安装使用switchyomega
  19. bpmnjs flowable 添加定时事件timeDate,timeDuration,timeCycle
  20. Unity HLOD System

热门文章

  1. 元宇宙系列之AI虚拟人:“人”潮汹涌 探路未来
  2. android 工具 Draw 9-patch 详解
  3. 位图(bitset)的使用【STL】
  4. PHP设计模式-简单工厂方法
  5. 团队任务3每日立会(2018-10-24)
  6. [幽默笑话]聪明男人劝老婆
  7. 比 Excel 更强大,Python 的可视化库 Altair 入门
  8. Mysql:Got error 28 from storage engine
  9. 表格内容单/多行展示(一)- 单行/多行显示的方法
  10. Windows远程连接电脑宿主机,管理服务器的几种快捷方式。