学习总结录 学习总结录
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
  • 案例实践

  • 工具使用

  • 项目搭建

  • 服务治理

  • ORM框架

  • MiniSpring

    • Spring IoC-原始Ioc
    • Spring IoC-扩展Bean
    • Spring IoC-属性注入
    • Spring Ioc-解决循环依赖
      • 一、Bean之间的依赖问题
        • 1、XML配置添加引用
        • 2、属性配置类添加引用
        • 3、解析XML额外处理引用
        • 4、Bean注入配置额外处理引用
      • 二、Spring循环依赖
        • 1、循环依赖概述
        • 2、解决思路
        • 3、具体方案
      • 参考
    • Spring Ioc-注解支持
    • Spinrg IoC-IoC完善
  • 案例归档
  • MiniSpring
旭日
2023-05-08
目录

Spring Ioc-解决循环依赖

# 一、Bean之间的依赖问题

# 1、XML配置添加引用

先前我们的属性注入都是基本类型,如果注入的属性本身就是一个对象,这就要涉及Bean之间的依赖问题。

Spring在标签里增加了ref属性,这个属性就记录了需要引用的另一个Bean,参考如下配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <bean id = "AService" class="me.xu.spring.test.demo02.AServiceImpl">
        <property name="content" type="String" value="hello A"/>
    </bean>
    <bean id = "BService" class="me.xu.spring.test.demo02.BServiceImpl">
        <constructor-arg name="content" type="String" value="hello b"/>
        <constructor-arg name="type"  type="int" value="1"/>
        <property name="aService" type="me.xu.spring.test.demo02.AServiceImpl" ref="AService"></property>
    </bean>
</beans>

首先我们定义了一个id为AService的Bean,然后我们在BService中设置ref="AService",表明我们希望注入的Bean不是一个简单的值,而是一个对象。

# 2、属性配置类添加引用

接下来需要在属性配置类添加引用字段,让其判断属性是引用类型还是普通类型。

public class PropertyValue {

    private final String name;
    private final String type;
    private final Object value;
    private final boolean isRef;

    public PropertyValue(String type, String name, Object value, boolean isRef) {
        this.type = type;
        this.name = name;
        this.value = value;
        this.isRef = isRef;
    }

    public String getType() {
        return this.type;
    }

    public String getName() {
        return this.name;
    }

    public Object getValue() {
        return this.value;
    }

    public boolean isRef() {
        return isRef;
    }
}

# 3、解析XML额外处理引用

			// 处理属性
            List<Element> propertyElements = element.elements("property");
            PropertyValues propertyValues = new PropertyValues();
            // Bean的依赖集合
            List<String> refs = new ArrayList<>();
            for (Element e : propertyElements) {
                // 获取属性
                String name = e.attributeValue("name");
                String type = e.attributeValue("type");
                String value = e.attributeValue("value");
                // 获取依赖
                String ref = e.attributeValue("ref");
                // 真实value
                String pV = "";
                boolean isRef = false;
                if (value != null && !"".equals(value)) {
                    pV = value;
                } else if (ref != null && !"".equals(ref)) {
                    isRef = true;
                    pV = ref;
                    refs.add(ref);
                }
                // 添加到属性集合中
                propertyValues.addPropertyValue(new PropertyValue(type, name, pV, isRef));
            }
            // 设置配置属性集合
            beanDefinition.setPropertyValues(propertyValues);
            String[] refArray = refs.toArray(new String[0]);
            // 设置依赖集合
            beanDefinition.setDependsOn(refArray);
  • 判断注入的属性是引用还是基本类型
  • 如果是基本类型,还是按照之前的处理逻辑
  • 如果是引用类型,就需要将具体Bean的真实value修改为引用类型,并且设置依赖集合

# 4、Bean注入配置额外处理引用

我们将之前createBean处理逻辑,单独抽离出来成为一个新方法:

private void handleProperty(BeanDefinition beanDefinition, Class<?> clz, Object obj) {
        // 处理属性
        PropertyValues propertyValues = beanDefinition.getPropertyValues();
        if (!propertyValues.isEmpty()) {
            // 遍历属性集合
            for (int i = 0; i < propertyValues.size(); i++) {
                PropertyValue propertyValue = propertyValues.getPropertyValueList().get(i);
                String pName = propertyValue.getName();
                String pType = propertyValue.getType();
                Object pValue = propertyValue.getValue();
                boolean isRef = propertyValue.isRef();

                Class<?>[] paramTypes = new Class<?>[1];
                Object[] paramValues = new Object[1];

                // 如果是基本类型
                if (!isRef) {
                    // 参数类型处理
                    if ("String".equals(pType) || "java.lang.String".equals(pType)) {
                        paramTypes[0] = String.class;
                    } else if ("Integer".equals(pType) || "java.lang.Integer".equals(pType)) {
                        paramTypes[0] = Integer.class;
                    } else if ("int".equals(pType)) {
                        paramTypes[0] = int.class;
                    } else {
                        paramTypes[0] = String.class;
                    }
                    // 参数具体数值
                    paramValues[0] = pValue;
                } else {
                    // 如果是引用类型
                    try {
                        paramTypes[0] = Class.forName(pType);
                        // 再次调用getBean方法
                        paramValues[0] = getBean((String)pValue);
                    } catch (ClassNotFoundException | BeansException e) {
                        throw new RuntimeException(e);
                    }
                }


                // 方法名字
                String methodName = "set" + pName.substring(0, 1).toUpperCase() + pName.substring(1);

                Method method = null;
                try {
                    // 获取到方法
                    method = clz.getMethod(methodName, paramTypes);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (SecurityException e) {
                    e.printStackTrace();
                }
                try {
                    // 反射调用方法
                    method.invoke(obj, paramValues);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }

            }
        }
    }

在这个方法中,除了之前对基本类型的处理逻辑外,额外增加对引用类型的处理逻辑,再次调用getBean创建ref的bean实例。

# 二、Spring循环依赖

# 1、循环依赖概述

在上面案例中,我们通过再次调用getBean方法,来对引用属性进行注入,但是如果引用注入的属性,又引用调用者本身,例如A注入B,B引用A,这样就会发生死循环,发生所谓的循环依赖问题,具体可以参考Spring循环依赖问题 (opens new window)。

# 2、解决思路

首先回忆一下创建Bean的过程:

  • 根据XML的Bean的定义生成BeanDefinition
  • 然后根据定义加载Bean类
  • 实例化Bean类
  • 完成属性注入

在属性注入之前,Bean的实例已经生成了,只是部分的属性没有注入,可以说现在这个Bean是一个半成品的Bean,最终注入完成的Bean,是放在单例对象集合中,也就是我们的singletons中,在实际的Spring中对应一级缓存,存放单例Bean对象集合。

现在为了解决循环依赖问题,我们需要一个集合来存放早期的Bean对象,后续如果存在循环依赖问题,就不再去实例化Bean对象,而是去获取早期的Bean对象,对应实际Spring中的三级缓存。

# 3、具体方案

添加半成品Bean集合

public class SimpleBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory, BeanDefinitionRegistry {
     /**
     * Bean半成品
     */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
}

相关方法

	public Object getBean(String beanName) throws BeansException {
        // 先尝试直接单例获取
        Object singleton = getSingleton(beanName);
        // 如果没有,开始实例化Bean
        if (singleton == null) {
            // 尝试从Bean半成品中获取
            singleton = earlySingletonObjects.get(beanName);
            if (singleton == null) {
                // 如果半成品也没有,创建Bean实例并注册
                // 获取Bean定义
                BeanDefinition beanDefinition = beanDefinitions.get(beanName);
                if (beanDefinition == null) {
                    throw new BeansException("错误Bean id");
                }
                try {
                    // 反射实例化Bean
                    singleton = createBean(beanDefinition);
                    // 注册单例Bean
                    registerSingleton(beanName, singleton);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return singleton;
    }
  • 先尝试直接单例获取
  • 如果没有,开始实例化Bean
  • 尝试从半成品Bean中获取
  • 如果半成品也没有,创建Bean实例并注册
    private Object createBean(BeanDefinition beanDefinition) {
        // 类名
        Class<?> clz = null;
        // 创建半成品Bean
        Object obj = doCreateBean(beanDefinition);
        // 放入半成品Bean集合中
        earlySingletonObjects.put(beanDefinition.getId(), obj);
        try {
            // 反射获取类名
            clz = Class.forName(beanDefinition.getClassName());
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 处理配置参数(属性注入)
        handleProperty(beanDefinition, clz, obj);
        return obj;
    }
  • 创建半成品Bean
  • 将半成品Bean放入earlySingletonObjects集合中
  • 调用handleProperty方法进行属性注入
private Object doCreateBean(BeanDefinition beanDefinition) {
        Class clz = null;
        Object obj = null;
        Constructor constructor = null;

        // 处理构造器参数
        ArgumentValues argumentValues = beanDefinition.getConstructorArgumentValues();
        if (!argumentValues.isEmpty()) {
            // 参数类型
            Class<?>[] paramTypes = new Class<?>[argumentValues.getArgumentCount()];
            // 参数值
            Object[] paramValues = new Object[argumentValues.getArgumentCount()];
            // 遍历构造器参数集合
            for (int i = 0; i < argumentValues.getArgumentCount(); i++) {
                ArgumentValue argumentValue = argumentValues.getIndexedArgumentValue(i);
                if ("String".equals(argumentValue.getType()) || "java.lang.String".equals(argumentValue.getType())) {
                    paramTypes[i] = String.class;
                    paramValues[i] = argumentValue.getValue();
                } else if ("Integer".equals(argumentValue.getType()) || "java.lang.Integer".equals(argumentValue.getType())) {
                    paramTypes[i] = Integer.class;
                    paramValues[i] = Integer.valueOf((String) argumentValue.getValue());
                } else if ("int".equals(argumentValue.getType())) {
                    paramTypes[i] = int.class;
                    paramValues[i] = Integer.valueOf((String) argumentValue.getValue()).intValue();
                } else {
                    paramTypes[i] = String.class;
                    paramValues[i] = argumentValue.getValue();
                }
            }
            try {
                // 获取构造函数
                constructor = clz.getConstructor(paramTypes);
                // 利用构造函数反射实例化Bean
                obj = constructor.newInstance(paramValues);
            } catch (NoSuchMethodException | InvocationTargetException | IllegalArgumentException | SecurityException e) {
                e.printStackTrace();
            } catch (InstantiationException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        } else {
            try {
                obj = clz.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return obj;
    }
  • 专门负责生成半成品Bean

现在我们再来回顾一下getBean()方法:

  • 首先要判断有没有已经创建好的 bean,有的话直接取出来,
  • 如果没有就检查 earlySingletonObjects 中有没有相应的半成品 Bean,
  • 有的话直接取出来,没有的话就去创建,并且会根据 Bean 之间的依赖关系把相关的 Bean 全部创建好。

# 参考

手把手带你写一个 MiniSpring (opens new window)

#Spring
上次更新: 2024/06/29, 15:13:44
Spring IoC-属性注入
Spring Ioc-注解支持

← Spring IoC-属性注入 Spring Ioc-注解支持→

最近更新
01
基础概念
10-31
02
Pytorch
10-30
03
Numpy
10-30
更多文章>
Theme by Vdoing | Copyright © 2021-2024 旭日 | 蜀ICP备2021000788号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式