之前博客的内容包括xml的加载,bean的解析和注册以及用户自定义标签的解析和注册都是容器初始化的内容.bean的加载时在这个基础上进行的,bean的加载过程比较复杂,设计的代码量很多.所以准备将bean的加载过程分为多个部分进行讲解.今天主要讲解bean的加载大致流程,之后会对每个步骤再进行细化讲解.
bean的加载的入口是通过调用工厂方法的getBean()开始.如下面代码所示:
1 | XmlBeanFactory bf = new XmlBeanFactory("bean.xml"); |
根据spring的习惯,一般将正式执行代码放在以do开头的方法里.getBean()内部的实现也是如此,是通过调用doGetBean()方法.代码如下:
1 | public Object getBean(String name) throws BeansException { |
getBean()和doGetBean的代码是在AbstractBeanFactory实现的.doGetBean的方法有些长,这里涵盖了bean加载的整个过程.如代码所示:
1 | protected <T> T doGetBean( |
结合以上注解,相信读者对于bean的加载大致过程都能理解.限制我们结合以上代码总结bean加载的大致步骤:
- 首先将beanName进行转化,这里转化的目的是将别名转化为具体的beanName. 因为spring里提供了别名的支持,用户在调用bean的时候可能调用的不是真实的beanName,而是调用别名.就像你朋友叫你时不一定都叫你名字,可能叫你外号,如小胖等.所以需要将外号转化为真实名字,spring里的名字转化做的也是这个工作.
- 尝试从缓冲或实例工厂中获取bean.该步骤主要时解决单例中的循环依赖问题.为了解决该问题,spring在创建bean的时候不等bean真正创建完成时将生成这个bean的ObjectFactory提前曝光,即保存到缓存中.一旦下一个bean的创建依赖当前bean时直接使用ObjectFacory.
- 如果缓冲中获取的bean不为空,那么指定bean的实例化. 因为缓冲中保存的bean是原始状态,不一定时我们想要的,所以这步骤主要是将缓冲中原始状态转化为最终想要的bean. 例如,缓冲中可能放的是生成bean的工厂,这时需要调用工厂方法生成对应的bean.
- 如果第二步从缓冲中取得的bean是空的,那进行真正的加载过程.首先判断该bean是否时原型并且是否存在循环依赖问题.
spring只为用户解决单例情况下的属性注入的循环依赖问题,对于其他循环依赖只能抛出异常.这些循环依赖包括原型循环依赖,单例情况下的构造器形成的循环依赖问题等. - 如果当前容器中不包含该bean,并且存在父工厂,则从父工厂中去加载.
- 将存储xml配置文件的GenericBeanDefinition转换为RootBeanDefinition,因为后续的处理都是针对RootBeanDefinition.
- 完成其所有依赖bean的加载.
- 针对不同的scope创建bean
- 最后进行类型转换,将bean转换为requireType所指定的类型.