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

spring源码(12)加载指定bean前,先加载依赖的bean_第1张图片

接着看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。看下面的例子:

id="beanOne" class="ExampleBean" depends-on="manager"/>
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分隔符,看下面的例子:

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


id="manager" class="ManagerBean" />
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> dependentBeanMap = new ConcurrentHashMap<>(64);

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

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

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


通过断点来试验一下:

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

    id="B" class="com.demo.app.Cat">

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

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

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

spring源码(12)加载指定bean前,先加载依赖的bean_第2张图片

spring源码(12)加载指定bean前,先加载依赖的bean_第3张图片

没啥问题!


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

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 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 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之间的销毁顺序

你可能感兴趣的:(spring源码,spring源码分析)