spring源码(12)加载指定bean前,先加载依赖的bean

这里写图片描述

接着看bean的加载过程,本节来看看如何递归实例化依赖的bean。

一、bean标签的depends-on属性

Spring Framework Reference Documentation 6.4.3. Using depends-on

该节详细介绍了 bean的depends-on,下面简单复习一下:

If a bean is a dependency of another that usually means that one bean is set as a property of another. 
Typically you accomplish this with the <ref/> element in XML-based configuration metadata.
 However, sometimes dependencies between beans are less direct; 
The depends-on attribute can explicitly force one or more beans to 
be initialized before the bean using this element is initialized. 
The following example uses the depends-on 
attribute to express a dependency on a single bean:

如果一个A bean是B bean的依赖,那么一般意味着A bean是B bean的一个属性。在XML配置文件中可以通过ref属性,将B bean设为A bean的属性。但是,这只是一般情况,还有一些情况虽然A bean是B bean的依赖,仅仅意味着在加载B bean时,要先加载A bean。看下面的例子:

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
To express a dependency on multiple beans, supply a list of bean names 
as the value of the depends-on attribute, with commas, whitespace and semicolons, used as valid delimiters:

如果想要表达依赖多个bean,可以使用逗号,空格和分号作为多个beand分隔符,看下面的例子:

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

特别注意:

在单例情况下,可以指定相互依赖bean之间的销毁顺序。
如果A bean是B bean的依赖,则在销毁B bean时,A bean先被销毁。


二、代码分析

String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
    for (String dep : dependsOn) {
        if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}
        registerDependentBean(dep, beanName);
        getBean(dep);
    }
}

首先获取所依赖的所有bean。
在DefaultSingletonBeanRegistry中有两个属性,分别是:

/** Map between dependent bean names: bean name --> Set of dependent bean names */
    private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);

    /** Map between depending bean names: bean name --> Set of bean names for the bean's dependencies */
    private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);

dependentBeanMap :key-被依赖的bean value-依赖者

dependenciesForBeanMap :key-依赖者 value-被依赖的bean


通过断点来试验一下:

<bean id="A" class="com.demo.app.Cat" depends-on="B;C"></bean>

    <bean id="B" class="com.demo.app.Cat"></bean>

    <bean id="C" class="com.demo.app.Cat" depends-on="B;D"></bean>

    <bean id="D" class="com.demo.app.Cat"></bean>
public static void main(String[] args) {

        BeanFactory bf = new XmlBeanFactory(new ClassPathResource("springContext.xml"));
        Cat cat = (Cat) bf.getBean("A");
    }

这里写图片描述

这里写图片描述

没啥问题!


接下来会判断是否循环依赖,如果是则抛出异常。

protected boolean isDependent(String beanName, String dependentBeanName) {
        return isDependent(beanName, dependentBeanName, null);
    }
private boolean isDependent(String beanName, String dependentBeanName, Set<String> alreadySeen) {
        if (alreadySeen != null && alreadySeen.contains(beanName)) {
            return false;
        }
        String canonicalName = canonicalName(beanName);
        Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
        if (dependentBeans == null) {
            return false;
        }
        if (dependentBeans.contains(dependentBeanName)) {
            return true;
        }
        for (String transitiveDependency : dependentBeans) {
            if (alreadySeen == null) {
                alreadySeen = new HashSet<>();
            }
            alreadySeen.add(beanName);
            if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
                return true;
            }
        }
        return false;
    }

在上面的代码中通过递归的方式来判断是否存在循环依赖。
假如 A 依赖 B,查看B是否也依赖A,如果是则返回false。

当然没有这简单。
假如:
A 依赖 B
C、D依赖 A ,
spring还会去判断 B与C,B与D是否循环依赖。

我也不知道应该用什么图来表达递归,很烦!

public void registerDependentBean(String beanName, String dependentBeanName) {
        // A quick check for an existing entry upfront, avoiding synchronization...
        String canonicalName = canonicalName(beanName);
        Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
        if (dependentBeans != null && dependentBeans.contains(dependentBeanName)) {
            return;
        }

        // No entry yet -> fully synchronized manipulation of the dependentBeans Set
        synchronized (this.dependentBeanMap) {
            dependentBeans = this.dependentBeanMap.get(canonicalName);
            if (dependentBeans == null) {
                dependentBeans = new LinkedHashSet<>(8);
                this.dependentBeanMap.put(canonicalName, dependentBeans);
            }
            dependentBeans.add(dependentBeanName);
        }
        synchronized (this.dependenciesForBeanMap) {
            Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName);
            if (dependenciesForBean == null) {
                dependenciesForBean = new LinkedHashSet<>(8);
                this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);
            }
            dependenciesForBean.add(canonicalName);
        }
    }

这段代码就是讲依赖关系存入dependentBeanMapdependenciesForBeanMap,很简单。


记录bean之间的依赖关系有两方面的作用:

1.避免循环依赖

2.在单例情况下,可以指定相互依赖bean之间的销毁顺序

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值