【spring原理】bean注入bean

为bean注入bean

根据github 项目学习:tiny-spring,地址:https://github.com/code4craft/tiny-spring

参考博客链接:https://blog.csdn.net/w8253497062015/article/details/90274387

目前的问题:无法处理bean之间的依赖,无法将bean注入到bean中,所以它无法称之为完整的IoC容器。

1.ref怎么实现?2.怎么解决xml中顺序问题?2.怎么避免循环依赖?

我们定义一个BeanReference,来表示这个属性是对另一个bean的引用。这个在读取xml的时候初始化,并在初始化bean的时候,进行解析和真实bean的注入。

public class BeanReference {
    private String name;
    private Object bean;
    
    public BeanReference(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Object getBean() {
        return bean;
    }
    public void setBean(Object bean) {
        this.bean = bean;
    }
}

实现 bean 的 ref

判断xml中是ref还是value,如果是value(本项目目前value如果是基本类型,只允许是String)则直接用PV(PropertyValue)封装,如果是ref,就用BeanReference{name,bean}封装一下然后再用PV封装。

private void processProperty(Element ele, BeanDefinition beanDefinition) {
    NodeList propertyNode = ele.getElementsByTagName("property");
    for (int i = 0; i < propertyNode.getLength(); i++) {
        Node node = propertyNode.item(i);
        if (node instanceof Element) {
            Element propertyEle = (Element) node;
            String name = propertyEle.getAttribute("name");
            String value = propertyEle.getAttribute("value");
            if (value != null && value.length() > 0) {
                beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
            } else {
                String ref = propertyEle.getAttribute("ref");
                if (ref == null || ref.length() == 0) {
                    throw new IllegalArgumentException("Configuration problem: <property> element for property '"
                            + name + "' must specify a ref or value");
                }
                BeanReference beanReference = new BeanReference(ref);
                beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanReference));
            }
        }
    }
}

在调用applyPropertyValues()方法——通过反射装填实例的成员变量时,如果该变量是BeanReference,则该变量有可能需要创建一下。

protected void applyPropertyValues(Object bean, BeanDefinition mbd) throws Exception {
    for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) {
        Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
        declaredField.setAccessible(true);
        Object value = propertyValue.getValue();
        if (value instanceof BeanReference) {
            BeanReference beanReference = (BeanReference) value;
            value = getBean(beanReference.getName());
        }
        declaredField.set(bean, value);
    }
}

读取xml后,所有的类信息都在 XmlBeanDefinitionReader 实例中,但是 XmlBDFR 中的 beanDefinition 们并没有创建实例,即空有类信息(className,PropertyValues),但是bean为null。
此时,如果遇到 A 实例 a 的 b 字段 ref C 实例 c,但是此刻 C 实例 c 还未初始化,在装配 A 实例a的b字段的时候,就会用 getBean 创建c。(为什么能创建c呢?因为在创建工厂后,紧接着的操作就是把xmlBDFR中的所有beanDefinition写入工厂的 ConcurrentHashMap 中,即工厂也有了全部的信息,因此可以创建c。)

通过getBean时创建实例的这种 lazy-init 方式,实现了不依靠 xml 中顺序。这样再创建实例的时候如果实例的依赖还没有创建,就先创建依赖。

循环依赖是类似以下的情况

<bean name="outputService" class="com.sonihr.beans.OutputService">
    <property name="helloWorldService" ref="helloWorldService"></property>
</bean>

<bean name="helloWorldService" class="com.sonihr.beans.HelloWorldServiceImpl">
    <property name="text" value="Hello World!"></property>
    <property name="outputService" ref="outputService"></property>
</bean>

在 doCreateBean 中,创建完空的bean (空的bean表示空构造函数构造出的bean) 后,就放入 beanDefinition 中,这样 a ref b,b ref a时,a ref b 因此 b 先创建并指向a,此时的 a 还不是完全体,但是引用已经连上了,然后创建好了b。然后b ref a的时候,a已经创建完毕。

发布了18 篇原创文章 · 获赞 0 · 访问量 271

猜你喜欢

转载自blog.csdn.net/shijyuan/article/details/105534186