手写Spring:第17章-通过三级缓存解决循环依赖

news2025/1/18 17:08:34

文章目录

  • 一、目标:通过三级缓存解决循环依赖
  • 二、设计:通过三级缓存解决循环依赖
    • 2.1 通过三级缓存解决循环依赖
    • 2.2 尝试使用一级缓存解决循环依赖
  • 三、实现:通过三级缓存解决循环依赖
    • 3.1 工程结构
    • 3.2 通过三级缓存解决循环依赖类图
    • 3.3 设置三级缓存
      • 3.3.1 对象工厂
      • 3.3.2 设置三级缓存
    • 3.4 提前暴露对象
      • 3.4.1 实例化感知对象处理
      • 3.4.2 默认自动代理创建者
      • 3.4.3 实现默认bean创建的抽象bean工厂超类
  • 四、测试:通过三级缓存解决循环依赖
    • 4.1 添加测试配置
      • 4.1.1 老公类
      • 4.1.2 媳妇类
      • 4.1.3 婆婆接口
      • 4.1.4 婆婆代理类
      • 4.1.5 切面
      • 4.1.6 Spring属性配置文件
    • 4.2 单元测试
  • 五、总结:通过三级缓存解决循环依赖

一、目标:通过三级缓存解决循环依赖

💡 如何解决类与类之间的循环依赖?

  • 在目前的 Spring 框架中,如果你配置了 A、B 两个 Bean 对象互相依赖,那么立刻会抛出 java.lang.StackOverflowError,为什么?
    • 因为 A 创建时需要依赖 B 创建,而 B 的创建又依赖于 A 创建,就这样死循环了。
  • 循环依赖 基本可以说是 Spring 中经典的实现,所要解决的场景主要三种情况。

在这里插入图片描述

  • 循环依赖主要分为三种:自身依赖于自身、互相循环依赖、多组循环依赖。
  • 但无论循环依赖的数量有多少,循环依赖的本质是一样。
    • 就是你的完整创建依赖于我,而我的完整创建也依赖于你,但我们互相没法解耦,最终导致依赖创建失败。
  • 所以需要 Spring 提供除了构造函数注入和原型注入外的,setter 循环依赖注入解决方案。

二、设计:通过三级缓存解决循环依赖

2.1 通过三级缓存解决循环依赖

💡 设计:通过三级缓存解决循环依赖

  • 用于解决循环依赖需要用到三个缓存,这三个缓存分别存放:
    • 成品对象、半成品对象(未填充属性值)、代理对象,分阶段存放对象内容,来解决循环依赖问题。
  • 需要知道核心原理:用于解决循环依赖就必须是三级缓存吗?二级缓存行吗?一级缓存行吗?
    • 其实都能解决。但是 Spring 框架的实现要保证几个事情。如:
      • 只有一级缓存处理流程没法拆分,复杂度也会增加,同时半成品对象可能会有空指针异常。
      • 而将半成品与成品对象分开,处理起来也更加优雅、简单、易扩展。
    • 另外 Spring 的两大特性中不仅有 IOC,还有 AOP,也就是基于字节码增强后的方法,该存放到哪。
      • 而三级缓存最主要,要解决的循环依赖就是对 AOP 的处理,但如果把 AOP 代理对象的创建提前,那么二级缓存也一样可以解决。
      • 但是,这就违背了 Spring 创建对象的原则,Spring 更喜欢把所有的普通 Bean 都初始化完成,再处理代理对象的初始化。
  • 思考:如果有对象不只是简单的对象,还有代理对象,还有 AOP 应用,要怎么处理这样的依赖问题。

在这里插入图片描述

  • 关于循环依赖在目前的 Spring 框架中,主要就是对于创建的提前暴露。
    • 如果是工厂对象则会使用 getEarlyBeanReference 逻辑提前将工厂对象存放到三级缓存中。
    • 等到后续获取对象的时候实际拿到的是工厂对象中 getObject,这个才是最终的实际对象。
  • 在创建对象的 AbstractAutowireCapableBeanFactory#doCreateBean 方法中,提前暴露对象以后。
    • 就可以通过接下来的流程,getSingleton 从三个缓存中以此寻找对象。
    • 一级、二级如果有则直接取走,如果对象是三级缓存中则会从三级缓存中获取后并删掉工厂对象,把实际对象放到二级缓存中。
  • 最后是关于单例的对象的注册操作,这个注册操作就是把真实的实际对象放到一级缓存中,因为此时它已经是一个成品对象了。

2.2 尝试使用一级缓存解决循环依赖

在这里插入图片描述

  • 如果仅以一级缓存解决循环依赖,那么在实现上可以通过 A 对象 newInstance 创建且未填充属性后,直接放入缓存中。
  • A 对象的属性填充 B 对象时,如果缓存中不能获取到 B 对象,则开始创建 B 对象,同样创建完成后,把 B 对象填充到缓存中。
  • 接下来就开始对 B 对象的属性进行填充,恰好这会可以从缓存中拿到半成品的 A 对象,那么这个时候 B 对象的属性就填充完了。
  • 最后返回来继续完成 A 对象的属性填充,把实例化后并填充了属性的 B 对象赋值给 A 对象的 b 属性,这样就完成了一个循环依赖操作。

CircleTest.java

package com.lino.springframework.test;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: 循环依赖案例
 */
public class CircleTest {

    private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    public static void main(String[] args) throws Exception {
        System.out.println(getBean(B.class).getA());
        System.out.println(getBean(A.class).getB());
    }

    private static <T> T getBean(Class<T> beanClass) throws Exception {
        String beanName = beanClass.getSimpleName().toLowerCase();
        if (singletonObjects.containsKey(beanName)) {
            return (T) singletonObjects.get(beanName);
        }
        // 实例化对象入缓存
        Object obj = beanClass.newInstance();
        singletonObjects.put(beanName, obj);
        // 属性填充补全对象
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            Class<?> fieldClass = field.getType();
            String fieldBeanName = fieldClass.getSimpleName().toLowerCase();
            field.set(obj, singletonObjects.containsKey(fieldBeanName) ? singletonObjects.get(fieldBeanName) : getBean(fieldClass));
            field.setAccessible(false);
        }
        return (T) obj;
    }
}

class A {

    private B b;

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }
}

class B {

    private A a;

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }
}

测试结果

com.lino.springframework.test.A@266474c2
com.lino.springframework.test.B@6f94fa3e

在这里插入图片描述

  • 从测试效果和截图依赖过程中可以看到,一级缓存也可以解决简单场景的循环依赖问题。
  • 其实 getBean,是整个解决循环依赖的核心内容,A 创建后填充属性时依赖 B,那么就去创建 B,在创建 B 开始填充时发现依赖于 A,但此时 A 这个半成品对象已经存放在缓存到 singletonObjects 中了,所以 B 可以正常创建,再通过递归把 A 也创建完整了。

三、实现:通过三级缓存解决循环依赖

3.1 工程结构

spring-step-16
|-src
	|-main
	|	|-java
	|		|-com.lino.springframework
	|			|-aop
	|			|	|-aspectj
	|			|	|	|-AspectJExpressionPointcut.java
	|			|	|	|-AspectJExpressionPointcutAdvisor.java
	|			|	|-framework
	|			|	|	|-adapter
	|			|	|	|	|-MethodBeforeAdviceInterceptor.java
	|			|	|	|-autoproxy
	|			|	|	|	|-DefaultAdvisorAutoProxyCreator.java
	|			|	|	|-AopProxy.java
	|			|	|	|-Cglib2AopProxy.java
	|			|	|	|-JdkDynamicAopProxy.java
	|			|	|	|-ProxyFactory.java
	|			|	|	|-ReflectiveMethodInvocation.java
	|			|	|-AdvisedSupport.java
	|			|	|-Advisor.java
	|			|	|-BeforeAdvice.java
	|			|	|-ClassFilter.java
	|			|	|-MethodBeforeAdvice.java
	|			|	|-MethodMatcher.java
	|			|	|-Pointcut.java
	|			|	|-PointcutAdvisor.java
	|			|	|-TargetSource.java
	|			|-beans
	|			|	|-factory
	|			|	|	|-annotation
	|			|	|	|	|-Autowired.java
	|			|	|	|	|-AutowiredAnnotationBeanPostProcessor.java
	|			|	|	|	|-Qualifier.java
	|			|	|	|	|-Value.java
	|			|	|	|-config
	|			|	|	|	|-AutowireCapableBeanFactory.java
	|			|	|	|	|-BeanDefinition.java
	|			|	|	|	|-BeanFactoryPostProcessor.java
	|			|	|	|	|-BeanPostProcessor.java
	|			|	|	|	|-BeanReference.java
	|			|	|	|	|-ConfigurableBeanFactory.java
	|			|	|	|	|-InstantiationAwareBeanPostProcessor.java
	|			|	|	|	|-SingletonBeanRegistry.java
	|			|	|	|-support
	|			|	|	|	|-AbstractAutowireCapableBeanFactory.java
	|			|	|	|	|-AbstractBeabDefinitionReader.java
	|			|	|	|	|-AbstractBeabFactory.java
	|			|	|	|	|-BeabDefinitionReader.java
	|			|	|	|	|-BeanDefinitionRegistry.java
	|			|	|	|	|-CglibSubclassingInstantiationStrategy.java
	|			|	|	|	|-DefaultListableBeanFactory.java
	|			|	|	|	|-DefaultSingletonBeanRegistry.java
	|			|	|	|	|-DisposableBeanAdapter.java
	|			|	|	|	|-FactoryBeanRegistrySupport.java
	|			|	|	|	|-InstantiationStrategy.java
	|			|	|	|	|-SimpleInstantiationStrategy.java
	|			|	|	|-xml
	|			|	|	|	|-XmlBeanDefinitionReader.java
	|			|	|	|-Aware.java
	|			|	|	|-BeanClassLoaderAware.java
	|			|	|	|-BeanFactory.java
	|			|	|	|-BeanFactoryAware.java
	|			|	|	|-BeanNameAware.java
	|			|	|	|-ConfigurableListableBeanFactory.java
	|			|	|	|-DisposableBean.java
	|			|	|	|-FactoryBean.java
	|			|	|	|-HierarcgicalBeanFactory.java
	|			|	|	|-InitializingBean.java
	|			|	|	|-ListableBeanFactory.java
	|			|	|	|-ObjectFactory.java
	|			|	|	|-PropertyPlaceholderConfigurer.java
	|			|	|-BeansException.java
	|			|	|-PropertyValue.java
	|			|	|-PropertyValues.java
	|			|-context
	|			|	|-annotation
	|			|	|	|-ClassPathBeanDefinitionScanner.java
	|			|	|	|-ClassPathScanningCandidateComponentProvider.java
	|			|	|	|-Scope.java
	|			|	|-event
	|			|	|	|-AbstractApplicationEventMulticaster.java
	|			|	|	|-ApplicationContextEvent.java
	|			|	|	|-ApplicationEventMulticaster.java
	|			|	|	|-ContextclosedEvent.java
	|			|	|	|-ContextRefreshedEvent.java
	|			|	|	|-SimpleApplicationEventMulticaster.java
	|			|	|-support
	|			|	|	|-AbstractApplicationContext.java
	|			|	|	|-AbstractRefreshableApplicationContext.java
	|			|	|	|-AbstractXmlApplicationContext.java
	|			|	|	|-ApplicationContextAwareProcessor.java
	|			|	|	|-ClassPathXmlApplicationContext.java
	|			|	|-ApplicationContext.java
	|			|	|-ApplicationContextAware.java
	|			|	|-ApplicationEvent.java
	|			|	|-ApplicationEventPublisher.java
	|			|	|-ApplicationListener.java
	|			|	|-ConfigurableApplicationContext.java
	|			|-core.io
	|			|	|-ClassPathResource.java
	|			|	|-DefaultResourceLoader.java
	|			|	|-FileSystemResource.java
	|			|	|-Resource.java
	|			|	|-ResourceLoader.java
	|			|	|-UrlResource.java
	|			|-stereotype
	|			|	|-Component.java
	|			|-util
	|			|	|-ClassUtils.java
	|			|	|-StringValueResolver.java
	|-test
		|-java
			|-com.lino.springframework.test
                |-bean
                |	|-Husband.java
                |	|-HusbandMother.java
				|	|-IMother.java
				|	|-SpouseAdvice.java
				|	|-Wife.java
                |-ApiTest.java
                |-CircleTest.java
		|-resources
			|-spring.xml

3.2 通过三级缓存解决循环依赖类图

在这里插入图片描述

  • 循环依赖的黑犀牛功能实现主要包括 DefaultSingletonBeanRegistry 提供三级缓存。
    • singletonObjectsearlySingletonObjectssingletonFactiries,分别存放成品对象、半成品对象和工厂对象。
    • 同时包装三个缓存提供方法:getSingletonregisterSingletonaddSingletonFactory,这样使用方就可以分别在不同时间段存放和获取对应的对象。
  • AbstractAutowireCapableBeanFactorydoCreateBean 方法中,提供了关于提前暴露对象的操作。
    • addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean))
    • 以及后续获取对象和注册对象的操作:exposedObject = getSingleton(beanName)registerSingleton(beanName, exposedObject)
    • 经过这样的处理就可以完成对复杂场景循环依赖的操作。
  • 另外在 DefaultAdvisorAutoProxyCreator 提供的切面服务中,也需要实现接口 InstantiationAwareBeanPostProcessor 新增的 getEarlyBeanReference 方法,便于把依赖的切面对象也能存放到三级缓存中,处理对应的循环依赖。

3.3 设置三级缓存

3.3.1 对象工厂

ObjectFactory.java

package com.lino.springframework.beans.factory;

import com.lino.springframework.beans.BeansException;

/**
 * @description: 对象工厂
 */
public interface ObjectFactory<T> {

    /**
     * 获取对象
     *
     * @return 泛型对象
     * @throws BeansException 异常
     */
    T getObject() throws BeansException;
}

3.3.2 设置三级缓存

DefaultSingletonBeanRegistry.java

package com.lino.springframework.beans.factory.support;

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.DisposableBean;
import com.lino.springframework.beans.factory.ObjectFactory;
import com.lino.springframework.beans.factory.config.SingletonBeanRegistry;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: 通用的注册表实现
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    protected static final Object NULL_OBJECT = new Object();
    /**
     * 一级缓存,普通对象
     */
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    /**
     * 二级缓存,提前暴露对象,没有完全实例化的对象
     */
    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 singletonObject = singletonObjects.get(beanName);
        if (null == singletonObject) {
            singletonObject = earlySingletonObjects.get(beanName);
            // 判断二级缓存中是否有对象,这个对象就是代理对象,因为只有代理对象才会放到三级缓存中
            if (null == singletonObject) {
                ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    // 把三级缓存中的代理对象中的真实对象获取出来,放入二级缓存中
                    earlySingletonObjects.put(beanName, singletonObject);
                    singletonFactories.remove(beanName);
                }
            }
        }
        return singletonObject;
    }

    @Override
    public void registerSingletonBean(String beanName, Object singletonObject) {
        singletonObjects.put(beanName, singletonObject);
        earlySingletonObjects.remove(beanName);
        singletonFactories.remove(beanName);
    }

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
        }
    }

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

    public void destroySingletons() {
        Set<String> keySet = this.disposableBeans.keySet();
        Object[] disposableBeanNames = keySet.toArray();

        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            Object beanName = disposableBeanNames[i];
            DisposableBean disposableBean = disposableBeans.remove(beanName);
            try {
                disposableBean.destroy();
            } catch (Exception e) {
                throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
            }
        }
    }
}
  • 在用于提供单例对象注册操作的 DefaultSingletonBeanRegistry 类中,共有三个缓存对象的属性:
    • singletonObjectsearlySingletonObjectssingletonFactories
    • 用于存放不同类型的对象:单例对象、早期的半成品单例对象、单例工厂对象。
  • 紧接着在这三个缓存对象下提供了获取、添加和注册不同对象的方法,包括:getSingletonregisterSingletonBeanaddSingletonFactory
    • 后面的两个方法都比较简单,主要是 getSingleton 的操作,它是一层层处理不同时期的单例对象,直至拿到有效的对象。

3.4 提前暴露对象

3.4.1 实例化感知对象处理

InstantiationAwareBeanPostProcessor.java

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

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValues;

/**
 * @description: 实例化感知对象处理
 */
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

    ...

    /**
     * 在 Spring 中由 SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference 提供
     *
     * @param bean     对象
     * @param beanName 对象名称
     * @return 二级缓存对象
     */
    default Object getEarlyBeanReference(Object bean, String beanName) {
        return bean;
    }
}
  • 添加 getEarlyBeanReference 缓存二级缓存对象方法

3.4.2 默认自动代理创建者

DefaultAdvisorAutoProxyCreator.java

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

import com.lino.springframework.aop.*;
import com.lino.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import com.lino.springframework.aop.framework.ProxyFactory;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValues;
import com.lino.springframework.beans.factory.BeanFactory;
import com.lino.springframework.beans.factory.BeanFactoryAware;
import com.lino.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import com.lino.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;

/**
 * @description: 默认自动代理创建者
 */
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private DefaultListableBeanFactory beanFactory;

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

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

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

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

    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 BeansException {
        return bean;
    }

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

    protected Object wrapIfNecessary(Object bean, String beanName) {
        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 getEarlyBeanReference(Object bean, String beanName) {
        earlyProxyReferences.add(beanName);
        return wrapIfNecessary(bean, beanName);
    }

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

3.4.3 实现默认bean创建的抽象bean工厂超类

AbstractAutowireCapableBeanFactory.java

package com.lino.springframework.beans.factory.support;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.PropertyValues;
import com.lino.springframework.beans.factory.*;
import com.lino.springframework.beans.factory.config.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @description: 实现默认bean创建的抽象bean工厂超类
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    private InstantiationStrategy instantiationStrategy = new SimpleInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        // 判断是否返回代理 Bean 对象
        Object bean = resolveBeforeInstantiation(beanName, beanDefinition);
        if (null != bean) {
            return bean;
        }
        return doCreateBean(beanName, beanDefinition, args);
    }

    protected Object doCreateBean(String beanName, BeanDefinition beanDefinition, Object[] args) {
        Object bean = null;
        try {
            // 实例化Bean
            bean = createBeanInstance(beanDefinition, beanName, args);

            // 处理循环依赖,将实例化后的Bean对象提前放入缓存中暴露出来
            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, bean, beanDefinition);
            // 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
            bean = initializeBean(beanName, bean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        // 注册实现 DisposableBean 接口的 Bean 对象
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

        // 判断 SCOPE_SINGLETON、SCOPE_PROTOTYPE
        Object exposedObject = bean;
        if (beanDefinition.isSingleton()) {
            // 获取代理对象
            exposedObject = getSingleton(beanName);
            registerSingletonBean(beanName, exposedObject);
        }
        return exposedObject;
    }

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

    ...
}
  • AbstractAutowireCapableBeanFactory#doCreateBean 的方法中主要是扩展了对象的提前暴露 addSingletonFactory,和单例对象的获取 getSingleton,以及注册操作 registerSingletonBean
  • getEarlyBeanReference 方法就是定义在如 AOP 切面中这样的代理对象。

四、测试:通过三级缓存解决循环依赖

4.1 添加测试配置

4.1.1 老公类

Husband.java

package com.lino.springframework.test.bean;

/**
 * @description: 老公类
 */
public class Husband {

    private Wife wife;

    public String queryWife() {
        return "Husband.wife";
    }

    public Wife getWife() {
        return wife;
    }

    public void setWife(Wife wife) {
        this.wife = wife;
    }
}

4.1.2 媳妇类

Wife.java

package com.lino.springframework.test.bean;

/**
 * @description: 媳妇类
 */
public class Wife {

    private Husband husband;
    private IMother mother;

    public String queryHusband() {
        return "Wife.husband、Mother.callMother: " + mother.callMother();
    }

    public Husband getHusband() {
        return husband;
    }

    public void setHusband(Husband husband) {
        this.husband = husband;
    }

    public IMother getMother() {
        return mother;
    }

    public void setMother(IMother mother) {
        this.mother = mother;
    }
}

4.1.3 婆婆接口

IMother.java

package com.lino.springframework.test.bean;

/**
 * @description: 婆婆接口
 */
public interface IMother {

    String callMother();
}

4.1.4 婆婆代理类

HusbandMother.java

package com.lino.springframework.test.bean;

import com.lino.springframework.beans.factory.FactoryBean;
import java.lang.reflect.Proxy;

/**
 * @description: 老公婆婆类
 */
public class HusbandMother implements FactoryBean<IMother> {

    @Override
    public IMother getObject() throws Exception {
        return (IMother) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IMother.class},
                (proxy, method, args) -> "婚后媳妇妈妈的职责被婆婆代理了!" + method.getName());
    }

    @Override
    public Class<?> getObjectType() {
        return IMother.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

4.1.5 切面

SpouseAdvice.java

package com.lino.springframework.test.bean;

import com.lino.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;

/**
 * @description: 拦截器
 */
public class SpouseAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("关怀小两口(切面):" + method);
    }
}

4.1.6 Spring属性配置文件

spring.xml

<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	         http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="husband" class="com.lino.springframework.test.bean.Husband">
        <property name="wife" ref="wife"/>
    </bean>

    <bean id="husbandMother" class="com.lino.springframework.test.bean.HusbandMother"/>

    <bean id="wife" class="com.lino.springframework.test.bean.Wife">
        <property name="husband" ref="husband"/>
        <property name="mother" ref="husbandMother"/>
    </bean>

    <!--AOP 配置,验证三级缓存    -->
    <bean class="com.lino.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

    <bean id="beforeAdvice" class="com.lino.springframework.test.bean.SpouseAdvice"/>

    <bean id="methodInterceptor" class="com.lino.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor">
        <property name="advice" ref="beforeAdvice"/>
    </bean>

    <bean id="pointcutAdvisor" class="com.lino.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
        <property name="expression" value="execution(* com.lino.springframework.test.bean.Wife.*(..))"/>
        <property name="advice" ref="methodInterceptor"/>
    </bean>

</beans>

4.2 单元测试

ApiTest.java

@Test
public void test_autoProxy() {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
    Husband husband = applicationContext.getBean("husband", Husband.class);
    Wife wife = applicationContext.getBean("wife", Wife.class);
    System.out.println("老公的媳妇:" + husband.queryWife());
    System.out.println("媳妇的老公:" + wife.queryHusband());
}

测试结果

老公的媳妇:Husband.wife
关怀小两口(切面)public java.lang.String com.lino.springframework.test.bean.Wife.queryHusband()
媳妇的老公:Wife.husband、Mother.callMother: 婚后媳妇妈妈的职责被婆婆代理了!callMother

在这里插入图片描述

  • 测试结果看,无论是简单对象依赖,还是代理工程对象,或者 AOP 切面对象,都可以在三级缓存下解决循环依赖的问题。
  • 此外从运行截图 DefaultSingletonBeanRegistry#getSingleton 中也可以看到需要三级缓存存放工厂对象的类,都会使用到 getObject 获取真实对象,并随后存入半成品对象 earlySingletonObjects 中以及移除工厂对象。

五、总结:通过三级缓存解决循环依赖

  • Spring 中所有的功能都是以解决 Java 编程中的特性而存在的,就像我们处理的循环依赖,如果没有 Spring 框架的情况下,可能我们也会尽可能避免写出循环依赖的操作,因为在没有经过加工处理后,这样的依赖关系肯定会报错的。
    • 这也是程序从 能用好用 的升级。
  • 在解决循环依赖的核心流程中,主要是提前暴露对象的设计,以及建立三级缓存的数据结构来存放不同时期的对象。
    • 如果说没有如切面和工厂中的代理对象,那么二级缓存也就可以解决了,哪怕是只有一级缓存。
    • 但为了设计上的合理和可扩展性,所以创建了三级缓存来放置不同时期的对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/997628.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kafka3.0.0版本——消费者(手动提交offset)

目录 一、消费者&#xff08;手动提交 offset&#xff09;的概述1.1、手动提交offset的两种方式1.2、手动提交offset两种方式的区别1.3、手动提交offset的图解 二、消费者&#xff08;手动提交 offset&#xff09;的代码示例2.1、手动提交 offset&#xff08;采用同步提交的方式…

JavaScript-----jQuery

目录 前言&#xff1a; 1. jQuery介绍 2. 工厂函数 - $() jQuery通过选择器获取元素&#xff0c;$("选择器") 过滤选择器&#xff0c;需要结合其他选择器使用。 3.操作元素内容 4. 操作标签属性 5. 操作标签样式 6. 元素的创建,添加,删除 7.数据与对象遍历…

torch.cuda.is_available() 解决方

本人使用的显卡如下&#xff0c;打开任务管理器查看 Anaconda下载哪个版本都可以 使用命令conda create -n pytorch python3.6创建一个名为pytorch的环境&#xff0c;解释器使用3.6的 使用命令conda activate pytorch进入该环境 进入pytorch官网&#xff0c;选择下列选项 复…

九 动手学深度学习v2 ——卷积神经网络之AlexNet

文章目录 AlexNetVGG AlexNet AlexNet新引入dropout、ReLU、maxpooling和数据增强。 VGG VGG神经网络连接 图7.2.1的几个VGG块&#xff08;在vgg_block函数中定义&#xff09;。其中有超参数变量conv_arch。该变量指定了每个VGG块里卷积层个数和输出通道数。全连接模块则与Ale…

超详细最新PyCharm+Python环境安装,多图,逐步骤

PyCharmPython环境安装 前言一、pycharm下载安装1. 安装地址2. 安装详细步骤 二、Python下载安装1. 安装地址2. 安装详细步骤3. 环境变量忘记添加4. python安装成功测试 三. PyCharm上配置Python总结推荐文章 前言 文章会详细介绍PyCharmPython详细安装步骤&#xff0c;接下来…

【数据分享】2012-2023年全国范围的逐月NPP/VIIRS夜间灯光数据

在之前的文章中我们分享了2012-2022年全球范围逐年NPP/VIIRS夜间灯光数据&#xff01;以及2012-2022年中国范围的逐年NPP/VIIRS夜间灯光数据&#xff08;均可查看之前的文章获悉详情&#xff09;很多小伙伴询问有没有逐月的夜间灯光数据。 本次我们分享的是2012-2023年中国范围…

Flask 快速上手教程 — 了解与基本使用

Flask 快速上手教程 — 了解与基本使用 这篇博客是我刚接触 flask&#xff0c;研究文档时的一些记录与体会&#xff0c;希望对各位刚接触 flask 的朋友有所帮助。 且在此篇后&#xff0c;我还会另写一篇关于纯后端的 flask 教程&#xff0c;介绍一下如何使用 flask 创建一个较…

Oracle for Windows安装和配置——2.1.Oracle for Windows安装

​2.1.1. 准备Oracle软件 1)下载或拷贝安装软件 下载地址:otn.oracle.com或my oracle support。下载文件列表。具体如图2.1.1-1所示。图2.1.1-1 下载文件列表 --说明: 1)通过otn.oracle.com站点,可以免费下载用于安装的Oracle软件,但通常只能下载到Oracle各大版本的base…

切面(增强)的优先级

Component Aspect Order(value 10)//为增强类指定一个优先级的值,值越小,优先级越高,优先级越高的前置先执行,后置后执行,类似洋葱 为增强类指定一个优先级的值,值越小,优先级越高,优先级越高的前置先执行,后置后执行,类似洋葱 首先会执行前置通知,再执行目标方法,按照顺序和优…

并查集介绍和常用模板

并查集介绍和常用模板 前言&#xff1a; 并查集&#xff08;Union-find set 也叫Disjoint Sets&#xff09;是图论里面一种用来判断节点之间是否连通的数据结构&#xff0c;学会使用它可以处理一些跟节点连通性的问题。它有两个很重要的方法&#xff1a; Find(x)&#xff1a;…

2023软件设计师上半年真题解析(上午+下午)

上午试题 1.系统总线 计算机中&#xff0c;系统总线用于&#xff08;&#xff09;连接。 A&#xff0e;接口和外设 B&#xff0e;运算器,控制器和寄存器C&#xff0e;主存及外设部件 D&#xff0e;DMA控制器和中断控制器 总线可以划分为数据总线、地址总线和控制总线。系统总…

自然语言处理应用(一):情感分析

情感分析 随着在线社交媒体和评论平台的快速发展&#xff0c;大量评论的数据被记录下来。这些数据具有支持决策过程的巨大潜力。 情感分析&#xff08;sentiment analysis&#xff09;研究人们在文本中 &#xff08;如产品评论、博客评论和论坛讨论等&#xff09;“隐藏”的情…

面向OLAP的列式存储DBMS-16-[ClickHouse]python操作ClickHouse

clickhouse查询表容量方法 1 clickhouse常用命令 #clickhouse-client进入客户端 pda1:)show databases; pda1:)create database test; pda1:)use system; pda1:)show tables; pda1:) exit; 其余的就是常规的一些sql语句。 2 python操作clickhouse 2.1 clickhouse-driver(9…

Windows下python,psycopg2使用sm3连接HGDB

瀚高数据库 目录 环境 文档用途 详细信息 环境 系统平台&#xff1a;N/A 版本&#xff1a;4.5 文档用途 本文介绍在HGDB使用sm3认证时&#xff0c;python使用psycopg2连接HGDB的方法。 详细信息 Python连接HGDB可以使用psycopg2、Django&#xff0c;Django是依赖psycopg2的&a…

微信小程序登录问题(思路简略笔记)

配置问题 这是小程序登录问题&#xff0c;必要的两个配置。 流程思路 1. 微信小程序端&#xff0c;会返回一个code。 2. 查看需要返回给微信小程序端的数据。 3. 既然需要返回三个数据&#xff0c;先看openid如何拿到 WX-Login https://api.weixin.qq.com/sns/jscode2ses…

C高级 Shell脚本

一、作业&#xff1a;写一个shell脚本&#xff0c;将以下内容放到脚本中 在家目录下创建目录文件&#xff0c;dir在dir下创建dir1和dir2把当前目录下的所有文件拷贝到dir1中&#xff0c;把当前目录下的所有脚本文件拷贝到dir2中把dir2打包并压缩为dir2.tar.xz再把dir2.tar.xz移…

2023-9-10 集合-Nim游戏

题目链接&#xff1a;集合-Nim游戏 #include <iostream> #include <cstring> #include <algorithm> #include <unordered_set>using namespace std;const int N 110, M 10010;int n, m; int s[N], f[M];int sg(int x) {if(f[x] ! -1) return f[x];//…

QT实战之翻金币游戏【未完待续】

文章目录 目录 文章目录 前言 二、创建项目 三、添加资源 四、主界面实现 1、设置游戏主场景配置 2、设置背景图片 3、创建开始按钮 总结 前言 对QT的相关知识与控件进行简单的学习之后&#xff0c;通过实现“翻金币游戏”来巩固与实践所学的QT知识。在制作过程中是根据以下视…

打开游戏显示xinput1_3.dll丢失怎么办?xinput1_3.dll最全修复方法分享

一、xinput1_3.dll 文件总体介绍 xinput1_3.dll 是 Microsoft DirectX 中的一个动态链接库文件&#xff0c;它主要负责处理游戏手柄、鼠标、键盘等输入设备的操作。当运行支持 DirectX 的游戏或程序时&#xff0c;xinput1_3.dll 文件会被操作系统加载到内存中&#xff0c;以提…

想要精通算法和SQL的成长之路 - 课程表II

想要精通算法和SQL的成长之路 - 课程表 前言一. 课程表II &#xff08;拓扑排序&#xff09;1.1 拓扑排序1.2 题解 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 课程表II &#xff08;拓扑排序&#xff09; 原题链接 1.1 拓扑排序 核心知识&#xff1a; 拓扑排序是专…