Spring 技术内幕之 IOC容器 (初版)

首先BeanDefinitionReader 会读取、解析配置文件,BeanDefinition实现类,其中包含了Class名称,id名称,别名信息,properties属性,是否懒加载,是否是原型模式,init-method信息,destory-method信息 等这些信息. 其中别名信息的话,是储存在一个map中的,其中key是beanName,value是BeanDefinition对象,别名的话就是会再将别名当做key,然后value是beanName,这样用别名的话就等于加一个重定向就可以定位到beanName 及对应的BeanDefinition. 再然后,会调用BeanFactoryPostProcessor(容器后置处理器)接口类的实现类,可以将之前注册的BeanDefinition的引用传递进去,因此此时还没有进行初始化bean操作,然后就可以手动注册一些BeanDefinition 或者 修改、移除一些BeanDefinition信息. 这里也是Spring留给我们的可扩展口,我们可以自己实现BeanFactoryPostProcessor接口,然后以XML配置文件的形式,将之以Bean 的形式注册到容器中,当容器启动的时候,就会读取并实例化出来,执行我们的方法.

再之后,就会调用BeanPostProcessor(Bean后置处理器)接口的实现类.这些后置处理器是每个Bean实例化过程中穿插执行的,其中的两个方法(postProcessBeforeInitialization,postProcessAfterInitialization)会在init-method方法前后进行增强,子接口还提供了在实例化前后增强的方法入口等.
其中Spring有一些内置的BeanPostProcessor ,比如 XXXXAware接口. 如果当前对象实现了这些接口,就会调用setXXXX方法,将对应的XXXX信息注入到Bean中. 还有@AutoWired , @Value 等注解的解析也是依赖这个接口.

然后会创建一个事件传播器实例对象,在Spring 生命周期内,会接收相应的事件并进行传播…事件传播器可以注册很多的Listener监听者,当IOC容器在进行一些操作之后,会把事件通知给事件传播器,然后事件传播器会将事件接收并传播给Listener,从而执行一些逻辑代码.
我们也可以实现接口,并以XML配置文件的形式将之注册到IOC 容器中,当IOC 容器启动的时候就会读取并实例化出来,从而执行我们的方法.

然后就是IOC 的核心Bean 的实例化,Bean实例化总体上分为三步: 初始化,属性注入和 实例化…
我们拿到BeanDefinition之后,会先将BeanDefinition转换为MergedBeanDefinition,这一步其实就是一个BeanDefinition继承,会先创建一个MergedBeanDefinition实例对象,然后将ParentBeanDefinition的信息拷贝到MergedBeanDefinition当中,然后再将当前BeanDefinition的信息覆盖到MergedBeanDefinition中,这样这个MergedBeanDefinition对象中就有了两者的信息.在往下走,就是看当前对象是否配置了depends-on依赖,如果依赖的话就先去实例化depends-on 的实例化对象,然后根据BeanDefinition中的指定的class信息去寻找适合的构造器,进行反射完成实例化操作. 到这里 bean实例化操作就基本结束了,后续还需要执行一些BeanPostProcessor接口的实现类方法来增强bean.

其中实例化bean 的时候有一种 比较复杂的情况就是循环依赖(循环依赖,当要实例化A 的时候,检查发现依赖B,而实例化B 的时候又发现需要依赖A ,这就是循环依赖)., 循环依赖有三种: 原型模式循环依赖,单例有参构造器注入循环依赖,单例setter注入依赖. 其中Spring 只能解决单例setter注入的循环依赖.
Spring检测循环依赖的机制:
1, 原型模式: 还是上面的例子,A -->B B–> A , 当我们需要实例化A 的时候,我们会先用getBean(A) 去缓存中查找A ,在缓存中找不到A 的时候,会在当前线程的threadLocals属性中的一个创建集合 set中,记录我们要实例化A ,然后, 初始化A,利用反射找到对应的构造方法,这个时候会发现依赖B ,然后就会getBean(B)去尝试在缓存中查找B ,找不到B 的话,就会在当前线程的threadLocals属性中的一个创建集合 set中记录要实例化B ,然后利用反射去找到对应的构造方法,这个时候就会发现依赖A ,然后getBean(A) 依然不会找到,就要再次记录要创建A 到set中,这个时候就会添加失败,因为set中已经记录了要创建A ,这样,Spring就会报出错误.提示循环依赖错误.
2.单例的有参构造注入循环依赖.跟原型模式相差不大.,只不过这里记录创建bean 的set集合不是放入threadLocals里面的了,而是一个普通的set中.

Spring 能解决单例setter注入的循环依赖: 当我们需要实例化A 的时候,我们会先用getBean(A) 去缓存中查找A ,在缓存中找不到A 的时候,会在一个set集合中记录我们要实例化A ,然后利用反射找到A的无参构造器进行初始化,此时的初始化的A的实例相当于是提前曝光的A实例,只是一个早产儿,我们将之包装成beanFactory放入三级缓存当中.然后这时候利用set方法进行属性注入的时候,会发现依赖B ,然后就会getBean(B),在缓存中查找B,发现没有,这时候就会在set中记录要实例化B ,然后利用反射找到B的无参构造器进行初始化,此时的初始化的B的实例就是提前曝光的B的实例,然后此时发现依赖A ,就用getBean(A),然后在三级缓存中找到A.而此时,就会调用A的objectFactory的getEarlyBeanReference方法,在方法中,会判断A是否有aop对其进行增强,如果有的话就会返回一个aop.a 给B注入,如果没有的话就会返回一个原型A 给B注入.
此时B属性注入成功之后,会进行实例化,如果之后有一系列的BeanPostProcessor操作,然后将B 的实例化对象放入到一级缓存当中
此时继续进行A 的属性注入,就会将B 注入到A中,然后A就会进行实例化和一系列的后置处理器的操作,而此时,不管是aop.a中的taget=a,还是原型a,他们的引用和此时完成实例化的A的引用都是一样的,所以值也是一样的,此时就完成了两个Bean 的实例化操作.解决了循环依赖的问题.

猜你喜欢

转载自blog.csdn.net/shiliu_baba/article/details/108043859