背景
spring 使用了 IOC(控制反转)模型,让对象存储在 spring 容器中,每当我们要使用对象时,就可以通过依赖注入的方式从容器中获取 bean 对象。
由于 bean 对象是交给容器来管理,故有了生命周期的概念。这个生命周期不仅有容器的生命周期,还有 bean 对象的生命周期。
spring 的容器
spring 的容器有 BeanFactory(bean 工厂)和 ApplicationContext (应用上下文)两个容器。
区别:
- beanfactory 只提供了基础的 bean 存储功能和依赖注入功能,版本控制功能。
- applicationContext 继承了 beanfactory 的所有特性,还提供了大量的扩展,根据配置实现国际化,事件传播,资源加载等,还支持 J2EE 的一些特性。
- BeanFactory 的 bean 是延时加载的,即创建容器时并不会实例化扫描到的对象,而是当被注入使用时才实例化。
- ApplicationContext 是非延时加载,即初始化容器是就把 bean 对象也初始化了。
bean 的生命周期
- 实例化 spring 容器(applicationContext)
- 扫描符合 springbean 规则的类(规则:@ComponentScan,@Bean 等)
- 遍历扫描的类,将其封装到一个 beanDefinitionMap 对象中。(可以理解为图纸,里面存放了 bean 类的信息,例如抽象类,懒加载等)
- 如果使用到了 BeanFactoryProcessor 类,则会来干预原先的类定义,即干预图纸。
- 遍历 beanDefinitionMjap(图纸),获取到类的信息,在通过算法推断出该类合理的构造方法。
- 通过这个构造方法反射出一个对象。
- 三级缓存,执行循环依赖注入。(下面会解释)
- 执行 aware 接口(BeanNameAware,BeanClassLoaderAware,BeanFactoryAware)
- 若 bean 关联了 BeanPostProcessor 接口,则此时会调用 postProcessBeforeInitialization 方法,主要是对 bean 的内容进行更改。
- 执行 bean 的初始化方法 init-method。
- 若 bean 关联了 BeanPostProcessor 接口,则还会调用 postProcessAfterInitialization 方法。
- 执行 aop 动态代理(例如事件分发,发布监听等)
- 将 bean 放入单例池 singletonobjects 中。
- 销毁,destroy()。
依赖注入
依赖注入也是循环依赖,是两个或两个以上的 bean 互相持有对方,形成闭环。比如 A 依赖 B,B 又依赖 A。
问题:当 A 初始化时,由于没有 B,故初始化不了;当 B 初始化时,由于没有 A,也初始化不了,造成了死锁。
那么如何才能在 spring 容器中创建这两个 bean 对象呢?spring 采用了三级缓存的概念。
三级缓存:
- singletonFactories:单例对象工厂,生产 bean 对象的地方。
- earlySingletonObjects:提前曝光的单例对象(还没初始化完,但已经有了雏形)
- singletonObjects:可以直接拿来用的缓存。
步骤:
- A 初始化时,就会在工厂中实例化并放入到提前曝光的单例对象缓存中。
- A 要注入属性 B 时,由于找不到 B 对象,故要先创建 B。
- B 初始化时,也会在工厂中实例化后放入到提前曝光的单例对象缓存中。
- B 要注入属性 A,先从单例对象缓存中找,没找到,再从提前曝光的缓存中找,找到了 A,就把 A 注入。
- B 完成创建后再创建 A。
- 由于 B 注入的是 A 的引用,A 在初始化后的操作对 B 也是可见的。