spring源码(12)加载指定bean前,先加载依赖的bean
2017-08-15 23:20
357 查看
接着看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); } }
这段代码就是讲依赖关系存入dependentBeanMap和dependenciesForBeanMap,很简单。
记录bean之间的依赖关系有两方面的作用:
1.避免循环依赖
2.在单例情况下,可以指定相互依赖bean之间的销毁顺序
相关文章推荐
- Spring IOC/BeanFactory/ApplicationContext的工作流程/实现原理/初始化/依赖注入源码详解
- 【spring源码分析】加载bean过程(1)
- Spring源码学习--Bean对象循环依赖问题解决(四)
- Spring Bean加载源码解析
- Spring源码分析:非懒加载的单例Bean初始化前后的一些操作
- Spring源码阅读——Bean的加载和获取过程
- 看看Spring的源码——Bean加载过程
- Spring源码解析:简单容器中Bean的加载过程初探
- Spring源码(一)——Bean加载过程
- spring 源码探索--bean加载准备阶段
- Spring源码阅读——Bean的加载和获取过程
- Spring源码解析:Bean的实例化与依赖注入(四)
- 从spring源码角度分析循环依赖bean的组装
- Spring源码阅读——简单模拟Spring的控制反转IOC和依赖注入(Bean的加载和获取)
- Spring源码解析笔记4——bean的加载
- spring源码学习(六)Bean的加载(中)
- 分析spring源码第五(二)篇:Spring中Bean的加载
- Spring源码解析-bean的加载
- spring源码初步学习-bean的加载
- Spring源码解析之Bean的加载