概述
- IOC 的作用:解耦、单例缓存、Bean生命周期管理、父子容器
- IOC 工作要经历2个过程:启动预加载BeanDefination、实例化对象且缓存单例,传统BeanFactory的实现类是懒加载(loadClass时再实例化bean),而web容器实现是预加载(启动和实例化一起进行)。但是BeanDefination都是预加载的。
IOC的作用
- 解耦:
- 实现了类与类依赖关系的解耦,大家都注册在IOC容器当中,并不直接强耦合。有多种注入类的方式:xml配置、@Component 注解、@import、@Configuration等等
- 实现了类的依赖关系和代码的解耦:把类的依赖关系,可以不通过修改代码,而是通过修改 xml然后容器来实现,这给非开发人员带来了很多便利
- 单例缓存:基于容器注册,就可以灵活缓存,避免内存空间的浪费,提升创建bean的速度。如果通过new的方式,就无法做到。
- 预处理:对Bean生成进行干预(init、构造函数、属性赋值、对象销毁)等过程进行干预
- 父子容器:MVC的实现就是很好的例子,既有一定的隔离性,又可以复用父容器的类
IOC容器启动和创建Bean逻辑
总体逻辑:IOC容器启动时,把所有配置过的Bean都全部各自生成一个BeanDefinition,并存储到BeanDefinitionRegistry的beanDefinitionMap中;用户在通过BeanFactory接口的实现类去getBean的时候,会从beanDefinitionMap中获取到这个bean的BeanDefinition,然后通过反射生成一个Bean,默认单例的话,还会把这个实例化的bean存储在singletonObjects中,下次再getBean的时候就直接从singletonObjects中返回
- 在IOC容器启动的时候,去扫描指定的application.xml,从而获取到需要注册的Bean的信息,不管你后期会不会用到这个Bean,所有扫到的Bean都会各自封装成一个BeanDefinition
- 因为Spring默认是懒加载,只有程序getBean的时候才会真正的实例化这个Bean,而实例化Bean的时候需要知道这个Bean的BeanDefinition(因为BeanDefinition里面有这个Bean的所有相关信息),所以需要BeanDefinitionRegistry这个BeanDefinition的管理器,来判断这个Bean是否已经注册生成了BeanDefinition,否则只有遍历所有创建过的BeanDefinition,才能知道你要get的这个Bean是否已经生成BeanDefinition
- BeanDefinitionRegistry接口主要负责管理所有注册过的BeanDefinition,比如增删改查功能,他的实现类需要2个工具成员变量:beanDefinitionMap、singletonObjects(都是ConcurrentHashMap结构)
- beanDefinitionMap负责缓存所有注册过的BeanDefinition;singletonObjects为单例类的缓存器,每个通过BeanDefinitionRegistry创建出来的bean,都缓存在singletonObjects里面,除非是多例
- 而BeanFactory接口是面向用户的,实际上他只是一个触发器,用户调用getBean方法,会先去beanDefinitionMap查找这个Bean是否已经生成了BeanDefinition,没有就报错,有就根据BeanDefinition来反射创建一个Bean,因为默认是单例的,创建完之后就缓存在singletonObjects里面
Spring getBean流程的细节
- 缓存查询:查询singletonObjects中是否有缓存
- 实例化DependsOn的类:查询类A是否是dependsOn的类,有的话,需要先实例化dependsOn的类
- 创建一个空的实体类:读取BeanDefinition信息,获取空的构造函数,通过反射创建一个所有filed为null的实体类(这样在堆中就有一个引用了)然后把这个Bean保存到singletonFactories中(以参数的形式 把bean存到factory里面)
- 然后遍历该类A的依赖属性,一一赋值,如果属性值依赖的是某一个具体的类B的时候,就必须先实例化B类
- 最后把创建好的Bean缓存在singletonObjects中,再返回给调用者
Spring 生命周期管理
- 实例化Bean执行顺序:调用构造函数、属性值的set方法、init方法(如果配置了的话)、destory方法
- 类通过实现特定的接口,来实现在某个过程中进行干预
- Spring 也支持配置容器级别的 干预类,注入到IOC当中后,所有的类在创建过程中,都会收到干预
普通Java Bean的生命周期
比如main方法里面,new User()这个操作,就会从class文件里面加载一个User对象到JVM里。
JavaBean的生命周期:类加载、连接、验证、准备、初始化
- 其中包括类静态成员变量的初始化为默认值
- 静态成员变量设置为设置值或者静态代码块的执行
- 调用构造函数
另外Java 加载Bean的触发条件:
- new的方式直接创建
- 当前类的静态属性或者静态方法被调用
- 该类作为父类被加载等等