Spring IOC

是什么
https://www.cnblogs.com/yxh1008/p/6012230.html
Ioc—Inversion of Control,即“控制反转”
控制:传统Java程序,对象的获取权由接口调用类获取,而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建。
反转:反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

为什么
https://www.cnblogs.com/yxh1008/p/6012230.html
有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。比如一个springboot项目,有controller,service,dao三层,每一层的对象由IOC容器负责,各层的开发人员并不用关心如何实例化下一层的对象,实现三层之间的解耦。

IOC容器初始化
ioc容器分为两类:
实现BeanFactory接口的简单容器;
实现ApplicationContext接口的高级容器。
区别:BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。

https://www.cnblogs.com/chenjunjie12321/p/6124649.html
第一步 Resource定位
Resource是Sping中用于封装I/O操作的接口。正如前面所见,在创建spring容器时,通常要访问XML配置文件,除此之外还可以通过访问文件类型、二进制流等方式访问资源,还有当需要网络上的资源时可以通过访问URL,Spring把这些文件统称为Resource。

具体代码如下:

Resource getResource(String location); //通过提供的资源location参数获取Resource实例
ClassLoader getClassLoader(); // 获取ClassLoader,通过ClassLoader可将资源载入JVM

第二步 通过返回的resource对象,进行BeanDefinition的载入
BeanDefinition相当于一个数据结构,这个数据结构的生成过程是根据定位的resource资源对象中的bean而来的,这些bean在Spirng IoC容器内部表示成了的BeanDefintion这样的数据结构,IoC容器对bean的管理和依赖注入的实现都是通过操作BeanDefinition来进行的。

第三步,将BeanDefiniton注册到容器中
  最终Bean配置会被解析成BeanDefinition并与beanName,Alias一同封装到BeanDefinitionHolder类中, 之后beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition()),注册到DefaultListableBeanFactory.beanDefinitionMap中。之后客户端如果要获取Bean对象,Spring容器会根据注册的BeanDefinition信息进行实例化。

Bean的解析
doGetBean():
第一部分,首先先去singleton缓存中去找实例。由于我们例子中没有把我们的bean手动放入singletonObjects这个Map里面去,所以这里肯定没找到。

第二部分,然后是去获取该BeanFactory父Factory,希望从这些Factory中获取,如果该Beanfactory有父类,则希望用父类去实例化该bean,类似于JVM类加载的双亲委派机制。

第三部分,这一部分是我们关注的重点,这里我们将这一大部分再分为三个小的部分来进行分析:

先将目前的bean标记为的正在创建
再获取根据beanName得到对应bean在beanfactory中的beanDefinitionMap的BeanDefinition(上一节初始化beanFactory时存入的),然后去获取这个bean依赖的bean。如果依赖的bean还没有创建,则先创建依赖的bean,进行递归调用(这就是依赖注入Dependence Injection)。如果找不到依赖,则忽略。
最后如果是单例(Spring默认是单例),则调用createBean()这个方法进行Bean的创建。

createBean():
1.确保该bean的class是真实存在的,也就是该bean是可以classload可以找到加载的
2.调用doCreateBean方法开始对bean进行创建

doCreateBean():
doCreateBean方法,在这个方法里会做两件事:一是通过createBeanInstance这个方法创建bean,二是通过initializeBean方法初始化bean。

创建Bean
1.createBeanInstance方法,这块代码主要是再次对bean做安全检查并确定该bean有默认的构造函数。(构造器注入需提供对应构造函数,属性注入需有不带参数的构造器)
2.Spring通过反射的方法根据BeanDefinition创建出Bean的对象并返回。

初始化Bean

1.initializeBean方法,加载相关资源(比如BeanName、BeanClassLoader、BeanFactory等资源)。
2.调用applyBeanPostProcessorsBeforeInitialization方法用于构造方法执行之前再次修改Bean(BeanPostProcessor接口)。
3.通过invokeInitMethods调用自定义的初始化方法
4.调用applyBeanPostProcessorsAfterInitialization方法用于构造方法执行之前再次修改Bean(BeanPostProcessor接口)。

Bean的生命周期
这里写图片描述
要想让spring容器控制bean的生命周期,那么该bean必须是单例。

关于lazy-init
lazy-init 设置只对scop属性为singleton的bean起作用
如果一个bean的scope属性为scope=“pototype“时,即使设置了lazy-init=”false”,容器启动时不实例化bean,而是调用getBean方法是实例化的

关于bean循环依赖的问题
可以分为三种情况:
1.构造器注入,在解析bean时,doGetBean()会将bean A标记为正在创建中,A依赖B,就会去找B,B又依赖A,发现A也在创建中,就会抛出:
org.springframework.beans.factory.BeanCurrentlyInCreationException:
2.属性注入,单例模式,doGetBean()会将bean标记为正在创建中,A依赖B,Spring会将这个实例化结束的对象放到一个Map中,并且Spring提供了获取这个未设置属性的实例化对象引用的方法,这里和构造器注入不同,属性注入可实例化一个未设置属性的bean,之后再设置属性值,构造器注入在实例化时就设置属性值了,也就是说B并不能去引用未实例化完成且正在创建中的A
3.属性注入,多例模式,并没有一个去存多例模式下bean的map,也就没提供获取这个未设置属性的实例化对象引用的方法,依然会报:
org.springframework.beans.factory.BeanCurrentlyInCreationException:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值