IoC容器主要用于管理Bean的生命周期和对象间的关系,经过依赖注入(DI)对容器中的Bean所须要依赖的其余对象进行注入。而这一切都是在Ioc容器里边进行的,假设A对象依赖B对象,若是IoC容器里只有A没有B,那么将会抛出bean找不到的异常;或者说A对象不在IoC容器,而B对象在IoC容器,那么将达不到自动注入的效果。java
web
这里是父级抽象类里的方法,方法实现自更上一级的ConfigurableApplicationContext接口。spring
编程
而后会有一个刷新前的准备操做,好比记录刷新开始时间啦,一些状态变量啦,打印日志啦。缓存
session
OK,能够看到它属性解析器里有el表达式的 ${ } 和 : 符号,并且属性源列表里有两个类型的属性源,打开其中一个,有不少K/V键值对,结合各类类、属性的各类语义,看来我以前的猜测无误,确实是用于把变量解析出值的。多线程
行,下一个。app
1 // Tell the subclass to refresh the internal bean factory. 2 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
跟踪一下obtainFreshBeanFactory();jvm
函数式编程
兜兜转转,终于来到了解析xml定义的bean的代码位置。
OK,解读这一段:
首先,获取<bean>
接着,获取<bean>节点上的name属性;那么,name属性呢,会被做为别名,多个别名能够容许用英文半角符号下的逗号和分号进行分割(, ;)。
接下来,就是以(,;)分割name属性上的值,将每一个别名都分割出来,虽然它在内部对分割出来的每一个别名作了trim操做,但我仍是建议不要留有空格。
再接着,肯定beanName,若是id属性有的话,就用id属性,没有的话,就会从别名中抽出第一个别名做为beanName,我说的抽出,就是它里边的aliases.remove(0)。
而后呢,会进行一个对beanName的查重操做,这里边能够看出,beanName和aliase是存放在同一个命名空间的(Set<String>集合),所以,在上一步aliases须要用remove(),不然本身就会抛名称重复异常。
接下来都差很少,就是一些属性的解析,子节点的解析。而后都封装到org.springframework.beans.factory.support.DefaultListableBeanFactory的beanDefinitionMap属性里,固然还有不少其它的细节我没有去深究,水平有限,太细了脑壳会爆的。
进入方法后也能看到,Spring的源码里有用到Lambda表达式,以前有遇到过Java8以前版本Lambda报错,因而这里我尝试了把IDEA的Language level改成了7,发现也能正常运行,仔细回想,以前碰到的Lambda报错是由于IDEA容许写Lambda,可是因为选择的java编译器是Java8版本前的版本,因而编译不经过。那么这么一想,我现在加入的依赖其实是Spring已经编译好的jar包,也就是说Lambda表达式已经被编译成了字节码文件,即便把运行环境切换回Java8以前的版本,也是能够运行的。那么我大胆的猜想,Java8和以后推出的一些语法糖在编译后,一样能在低版本的jvm上运行(这个离题了,以后再试,到时候顺便去看看官方文档)。
我们继续分析上面的代码,最后一行就是初始化单例的操做了,那么在它的上一行,还有一个冻结配置的操做,代码粘贴出来,都能看懂,冻结后具体有什么用,咱先不看。
循环迭代全部定义的Bean,获取到Bean在Root容器中的定义 (这块的合并定义有点东西,有空再看仔细),接着就是判断Bean的类型是否为org.springframework.beans.factory.FactoryBean接口的派生类,若是是,还会通过一些系统权限特殊处理。固然,最后都会到达getBean(beanName)。
简单理解下:
1.尝试获取单例,若是单例已经存在了,那么作一些验证后没有问题直接返回这个单例,里边的逻辑还涉及一些工厂Bean和普通Bean的选择问题,也挺复杂的,就不细说。这其实已经能够证实了我上面的猜想了。
2.1若是示例并不存在,那么首先检查它是否是在构建中,若是已经在构建中了这里还来构建, 那可能已经进入了一个循环构建的状态了,这时候就会直接抛出【BeanCurrentlyInCreationException】,后边我会针对这个异常进行测试。
2.2先获取父级BeanFactory,若是存在父级的BeanFactory而且自身有没有该beanName对应的BeanDefinition的话,那用父级的BeanFactory来提供这个Bean,若是一直到Root级的容器也提供不了这个Bean,那就是【NoSuchBeanDefinitionException】了。那么这里的话能够参考上面的第7大点,建立BeanFactory的时候,会将父级容器里的BeanFactory做为本身的patentBeanFactory,那咱们这里的是Root级别的容器,BeanFactory也就是Root级别的,因此只能本身构建了。
2.3判断下是否要类型检查,通常都是false,我看来一下,只有一个地方用了true,位置在方法org.springframework.beans.factory.support.AbstractBeanFactory#getTypeForFactoryBean。 咱不深究,就以如今是false的状态继续吧,会执行markBeanAsCreated(beanName),这里在真正构造Bean以前,先记录一下Bean已经构建了,而后还把mergedBeanDefinitions集合里的Bean的定义给remove掉了。
2.4分析Bean定义开始,调用的getMergedLocalBeanDefinition(beanName),哇塞,上一步刚吧BeanDefinition从mergedBeanDefinitions集合中remove掉,这一步又给加回去了,感受删掉那一步有些画蛇添足,就像是个BUG,不过倒不会引发什么异常。
接下来注意了注意了,这里要开始构建了,若是忽略掉多级容器,多线程什么什么的,我以为这个位置算是最核心部分了,在我看来接下来这块就是IoC和DI的具体实现。
2.5从RootBeanDefinition定义中获取到构建依赖(dependsOn),这个dependsOn,其实就是在配置<bean>的时候能够配置一个"depend-on"这么一个属性,里边只能配置其余的beanName,若是这个属性有值,就会先等待依赖的beanName先构建好,再继续构建自身,若是配置的是本身,或者是依赖自身的其余类,那么就会陷入死循环,抛出异常好比:【BeanCreationException】。
2.6若是存在依赖,那么注册依赖关系,这依赖关系是双向维护的,若是A依赖B,那么A所需依赖里有B,B的被他依赖里有A。
2.7而后先把所需依赖给初始化了,也就是A依赖B,那么先把B给初始化了,这里就算是为DI作前置准备了。
2.8接着,就判断Bean的scope,若是是singleton,getSingleton
若是是prototype,那就从新构建一个Bean的实例。
这里咱先关注singleton,能够看到getSingleton()穿了两个参数,一个是beanName,一个是Lambda表达式,嗯,就是构建了一个匿名内部类的实例做为参数。嗯,函数式编程杠杠的。
2.8.1这里一样是,若是单例已经存在了,就直接返回,若是没有存在,就调用专属的ObjectFactory#getObject构建一个实例再返回。
再跟踪doCreateBean,
在一连串繁琐的处理后,终于仍是来到了BeanUtils……
先给构造器设置为可访问的,还检查一下是否为Kotlin的类,是就用Kotlin的方式构造实例,不是就用调用原生的newInstance。唉,再深刻就是反射的源代码了,前段时间刚看到一篇文章说反射的对象调用超过必定次数后会被生成class字节码加载到jvm里,今天看到了那段代码,可是让脑子缓缓吧,改天再研究反射的实现。
最后,对象构建完成后,还有属性的注入。
其中populateBean则是对属性进行赋值的,一直找到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyPropertyValues
会看到遍历属性赋值这一段:
若是有关联(即ref),则会在valueResolver.resolveValueIfNecessary(pv, originalValue);内进行检查。
再跟踪到resolveReference里:
能够看到它会在这里getBean,若是ref的bean已存在,天然会直接获得,若是没有存在,则会先初始化。嗯,而后上面的初始化操做又来一遍。
备注:
用于类型转换的类型修改器默认只有overriddenDefaultEditors,共12个,均为与IO相关的好比URL、InputSteam。
基本类型的修改器在首次查到没有时,才会建立,共47个,好比int、long、String,当前Spring版本为5.1.7.RELEASE。
① scope="singleton"时,Spring能处理好这个关系,成功容许。
解析成BeanDefinition的时候是不抛异常的,只有在运行到构建dependOn时,才会抛出异常【BeanCreationException】,不管scope是prototype仍是singleton。
一样,无论scope是prototype仍是singleton,都会抛出异常【BeanCurrentlyInCreationException】。
双方是 都是scope="prototype"时,抛出异常【BeanCurrentlyInCreationException】。只要有一方是 scope="singleton",则可正常运行。
那Role必须是单例的状况下,才能正常运行了。
从容器结构来看,理论上IoC容器能够达到无限嵌套,在子容器维护着父级容器的关系,父子容器各自定义的Bean的单例都会缓存在各自的BeanFactory的singletonObjects里,当子容器在singletonObjects中找不到Bean时,会往父容器里找,或者说子容器中能够定义新的Bean屏蔽掉父级的Bean,使得切换不一样的Bean实现能够更加灵活。可是呢,父容器由于没有维护与子容器的关系,所以父容器里是没法经过getBean获取到子容器的Bean的。
Bean的定义上,会将Bean的构建条件都解析封装到BeanDefinition中,才开始初始化单例。定义时,要规避死循环通常的依赖,也就是在实例化Bean前,避免依赖关系又回当前Bean。
② 容器初始化大体流程
水平有限,若是有哪里写的不对的,欢迎指出,我会及时改正,避免误导你们。