声明
本文的主要目的不是来讲解 Spring 的 depends-on 的相关概念和如何使用的, 更不是来分析源码的,
而是想借助本文的案例说明两点关于阅读源码的体会:
- 要想读通框架源码, 首先你得先玩儿通.
- 阅读源码过程中或者看了他人对源码的分析见解后, 自己也要去 debug 验证一下.
正文
自己用 Spring 做业务开发两年了, 但是从来没有接触或用到过它的 depends-on 特性.因此当在源码中看到这个词汇时, 很容易把它跟我们常用的 依赖注入 扯在一起, 最终导致自己对框架源码的理解产生了疑虑.
先回顾一下如何配置依赖注入
在配置 Spring 的 Bean 以及 Bean 之间的依赖关系时, 我们经常使用 xml 或者 注解 的方式. 下面我们用 xml 的方式回顾一下如何配置 Spring 的依赖注入.
假设我们有一个 User 对象, 这个 User 对象依赖一个 Car 对象. 于是我们有了如下的配置:
<!--Car-->
<bean class="com.demo.Car" id="car">
</bean>
<!-- User -->
<bean class="com.demo.User" id="user">
<property name="car" ref="car"/>
</bean>
再引入循环依赖的概念
还拿上面 User 和 Car 的来举例, 循环依赖 就是 User 对象依赖 Car 对象, Car 对象 又依赖了 User 对象, 从而造成了如下关系图:
因为我不知道 Spring 的 depends-on 用法, 半天没看懂源码
同时, 我们要了解到的是 Spring 对于单例 Bean,并且通过 setter 方法注入依赖的情况, 是可以解决掉 Spring 循环依赖的, 也就是说下面的配置关系是不会出错的.
<!--Car-->
<bean class="com.demo.Car" id="car">
<property name="user" ref="user"/>
</bean>
<!-- User -->
<bean class="com.demo.User" id="user">
<property name="car" ref="car"/>
</bean>
最后窥探下获取 Bean 的“源码”
完整的源码可以参考 org.springframework.beans.factory.support.AbstractBeanFactory#getBean
但是方便说明问题, 我们需要对 getBean 方法做如下简化, 同时基于我们上面对 Spring 依赖注入的了解, 自然而然就轻易给出了如下的注释:
public Object getBean(String beanName) {
// 1.先拿到 Bean 的定义
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 2.如果当前 bean 有依赖其他 Bean, 就先把依赖的 Bean 实例化
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
getBean(dep);
}
}
// TODO
}
通过分析上面的逻辑, 我们发现当遇见 循环依赖 时, 一旦执行到第二个步骤就会出现无限递归的情况.
这个时候头上就会出现一堆问号, dependsOn 就是依赖的意思啊, 难道我的理解有问题? 更难受的是当时因为一些条件限制, 我没办法去做 debug, 并且之前在看别人对 Spring 源码分析的相关文章时, 也几乎没人提到这段源码, 最后通过源码搜索, 才查到 Spring 中存在 dependsOn 的相关概念和用法. 具体的 Xml 的配置如下:
<bean id="car" class="com.demo.Car">
<bean id="user" class="User" depends-on="car"/>
同时, 这里对 depends-on 的用法给出一个简短的解释, 需要详细了解的, 请自行检索.
depends-on 适用于表面上看起来两个 bean 之间没有使用属性之类的强连接的 bean,但是两个 bean 又确实存在前后依赖关系的情况,使用了depends-on的时候,依赖他人的 bean 是先于被依赖 bean 销毁的
总而言之, 这个“依赖” 跟我们常说(用)的那个依赖注入是两回事.
总结
因为不知道 Spring 中 “depends-on” 用法的存在, 加上阅读源码时没能够第一时间去 debug, 导致在阅读 get Bean 源码过程中困惑了许久. 当然文中只说了 “depends-on” 这一个例子, 但是我们也不可能把 Spring 的所有用法都了解一遍再去看源码, 因此在借助一些文章阅读源码时, 既要自己做 debug 验证, 也得要求自己尽量对 Spring 的一些“不常见” 的用法和概念掌握后, 再去深度了解源码.
简单总结, 问题根源在于我们 “眼高手低” 的坏习惯和 ”用什么学什么的“ 的工作方法.
下面先简单列一些对于平常只用 Spring 写业务的人不大可能用到的一些类或概念:
-
Spring 的父子容器的概念
-
Spring 循环依赖的概念
-
Spring init-method 初始化方法
-
Aware 接口的用法
-
FactoryBean 接口的用法
-
BeanPostProcessor 接口的用法
-
BeanFactoryPostProcessor 接口的用法
-
InitializingBean 接口的用法
-
等等
同时, 下一篇文章将会对上述提到的概念和类做详细讲解, 写给 ”我们这一类人“.