接着看bean的加载过程,本节来看看如何递归实例化依赖的bean。
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");
}
没啥问题!
接下来会判断是否循环依赖,如果是则抛出异常。
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);
}
}
这段代码就是讲依赖关系存入dependentBeanMap和dependenciesForBeanMap,很简单。
记录bean之间的依赖关系有两方面的作用:
1.避免循环依赖
2.在单例情况下,可以指定相互依赖bean之间的销毁顺序