Bean的生命周期&加载过程
Bean的生命周期
看了上面那么多内容,是不是基本快完了?NO,NO,NO,重要的部分在这里!
- 当调用者通过 getBean(beanName)向容器请求某一个 Bean 时,如果容器注册了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor 接口,在实例化 Bean 之前,将调用接口的 postProcessBeforeInstantiation()方法;
- 根据配置情况调用 Bean 构造函数或工厂方法实例化 Bean;
- 如果容器注册了 InstantiationAwareBeanPostProcessor 接口,在实例化 Bean 之后,调用该接口的 postProcessAfterInstantiation()方法,可在这里对已经实例化的对象进行一些“梳妆打扮”;
- 如果 Bean 配置了属性信息,容器在这一步着手将配置值设置到 Bean 对应的属性中,不过在设置每个属性之前将先调用InstantiationAwareBeanPostProcessor 接口的postProcessPropertyValues()方法;
- 调用 Bean 的属性设置方法设置属性值;
- 如果 Bean 实现了 org.springframework.beans.factory.BeanNameAware 接口,将调用setBeanName()接口方法,将配置文件中该 Bean 对应的名称设置到 Bean 中;
- 如果 Bean 实现了 org.springframework.beans.factory.BeanFactoryAware 接口,将调用 setBeanFactory()接口方法,将 BeanFactory 容器实例设置到 Bean 中;
- 如果 BeanFactory 装配了 org.springframework.beans.factory.config.BeanPostProcessor后处理器,将调用 BeanPostProcessor 的 Object postProcessBeforeInitialization(Object bean, String beanName)接口方法对 Bean 进行加工操作。其中入参 bean 是当前正在处理的 Bean,而 beanName 是当前 Bean 的配置名,返回的对象为加工处理后的 Bean。用户可以使用该方法对某些 Bean 进行特殊的处理,甚至改变 Bean 的行为, BeanPostProcessor 在 Spring 框架中占有重要的地位,为容器提供对 Bean 进行后续加工处理的切入点, Spring 容器所提供的各种“神奇功能”(如 AOP,动态代理等)都通过 BeanPostProcessor 实施;
- 如果 Bean 实现了 InitializingBean 的接口,将调用接口的 afterPropertiesSet()方法;
- 如果在通过 init-method 属性定义了初始化方法,将执行这个方法;
- BeanPostProcessor 后处理器定义了两个方法:其一是 postProcessBeforeInitialization() 在第 8 步调用;其二是 Object postProcessAfterInitialization(Object bean, String beanName)方法,这个方法在此时调用,容器再次获得对 Bean 进行加工处理的机会;
- 如果在中指定 Bean 的作用范围为 scope=“prototype”,将 Bean 返回给调用者,调用者负责 Bean 后续生命的管理, Spring 不再管理这个 Bean 的生命周期。如果作用范围设置为 scope=“singleton”,则将 Bean 放入到 Spring IoC 容器的缓存池中,并将 Bean引用返回给调用者, Spring 继续对这些 Bean 进行后续的生命管理;
- 对于 scope=“singleton”的 Bean,当容器关闭时,将触发 Spring 对 Bean 的后续生命周期的管理工作,首先如果 Bean 实现了 DisposableBean 接口,则将调用接口的afterPropertiesSet()方法,可以在此编写释放资源、记录日志等操作;
- 对于 scope=“singleton”的 Bean,如果通过的 destroy-method 属性指定了 Bean 的销毁方法, Spring 将执行 Bean 的这个方法,完成 Bean 资源的释放等操作。
可以将这些方法大致划分为三类:
- Bean 自身的方法:如调用 Bean 构造函数实例化 Bean,调用 Setter 设置 Bean 的属性值以及通过的 init-method 和 destroy-method 所指定的方法;
- Bean 级生命周期接口方法:如 BeanNameAware、 BeanFactoryAware、 InitializingBean 和 DisposableBean,这些接口方法由 Bean 类直接实现;
- 容器级生命周期接口方法:在上图中带“★” 的步骤是由 InstantiationAwareBean PostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“ 后处理器” 。后处理器接口一般不由 Bean 本身实现,它们独立于 Bean,实现类以容器附加装置的形式注册到 Spring 容器中并通过接口反射为 Spring 容器预先识别。当Spring 容器创建任何 Bean 的时候,这些后处理器都会发生作用,所以这些后处理器的影响是全局性的。当然,用户可以通过合理地编写后处理器,让其仅对感兴趣Bean 进行加工处理
ApplicationContext 和 BeanFactory 另一个最大的不同之处在于:ApplicationContext会利用 Java 反射机制自动识别出配置文件中定义的 BeanPostProcessor、 InstantiationAwareBeanPostProcessor 和 BeanFactoryPostProcessor,并自动将它们注册到应用上下文中;而后者需要在代码中通过手工调用 addBeanPostProcessor()方法进行注册。这也是为什么在应用开发时,我们普遍使用 ApplicationContext 而很少使用 BeanFactory 的原因之一。
这是对Bean的生命周期解释的最详细的一篇博文,没有之一,这个八股文就需要多看几遍,虽然有些地方还不能完全理解,我打算后面再看看源码,应该就能理解了。
Bean加载过程
Spring的高明之处在于,它使用众多接口描绘出了所有装置的蓝图,构建好Spring的骨架,继而通过继承体系层层推演,不断丰富,最终让Spring成为有血有肉的完整的框架。所以查看Spring框架的源码时,有两条清晰可见的脉络:
- 接口层描述了容器的重要组件及组件间的协作关系;
- 继承体系逐步实现组件的各项功能。
接口层清晰地勾勒出Spring框架的高层功能,框架脉络呼之欲出。有了接口层抽象的描述后,不但Spring自己可以提供具体的实现,任何第三方组织也可以提供不同实现, 可以说Spring完善的接口层使框架的扩展性得到了很好的保证。纵向继承体系的逐步扩展,分步骤地实现框架的功能,这种实现方案保证了框架功能不会堆积在某些类的身上,造成过重的代码逻辑负载,框架的复杂度被完美地分解开了。
Spring组件按其所承担的角色可以划分为两类:
- 物料组件:Resource、BeanDefinition、PropertyEditor以及最终的Bean等,它们是加工流程中被加工、被消费的组件,就像流水线上被加工的物料;
- BeanDefinition:Spring通过BeanDefinition将配置文件中的配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器的后续操作直接从BeanDefinitionRegistry中读取配置信息。
- 加工设备组件:ResourceLoader、BeanDefinitionReader、BeanFactoryPostProcessor、InstantiationStrategy以及BeanWrapper等组件像是流水线上不同环节的加工设备,对物料组件进行加工处理。
- InstantiationStrategy:负责实例化Bean操作,相当于Java语言中new的功能,并不会参与Bean属性的配置工作。属性填充工作留待BeanWrapper完成
- BeanWrapper:继承了PropertyAccessor和PropertyEditorRegistry接口,BeanWrapperImpl内部封装了两类组件:(1)被封装的目标Bean(2)一套用于设置Bean属性的属性编辑器;具有三重身份:(1)Bean包裹器(2)属性访问器 (3)属性编辑器注册表。PropertyAccessor:定义了各种访问Bean属性的方法。PropertyEditorRegistry:属性编辑器的注册表。
该图描述了Spring容器从加载配置文件到创建出一个完整Bean的作业流程:
- ResourceLoader从存储介质中加载Spring配置信息,并使用Resource表示这个配置文件的资源;
- BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中每一个解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中;
- 容器扫描BeanDefinitionRegistry中的BeanDefinition,使用Java的反射机制自动识别出Bean工厂后处理后器(实现BeanFactoryPostProcessor接口)的Bean,然后调用这些Bean工厂后处理器对BeanDefinitionRegistry中的BeanDefinition进行加工处理。主要完成以下两项工作:
- 对使用到占位符的元素标签进行解析,得到最终的配置值,这意味对一些半成品式的BeanDefinition对象进行加工处理并得到成品的BeanDefinition对象;
- 对BeanDefinitionRegistry中的BeanDefinition进行扫描,通过Java反射机制找出所有属性编辑器的Bean(实现java.beans.PropertyEditor接口的Bean),并自动将它们注册到Spring容器的属性编辑器注册表中(PropertyEditorRegistry);
- Spring容器从BeanDefinitionRegistry中取出加工后的BeanDefinition,并调用InstantiationStrategy着手进行Bean实例化的工作;
- 在实例化Bean时,Spring容器使用BeanWrapper对Bean进行封装,BeanWrapper提供了很多以Java反射机制操作Bean的方法,它将结合该Bean的BeanDefinition以及容器中属性编辑器,完成Bean属性的设置工作;
- 利用容器中注册的Bean后处理器(实现BeanPostProcessor接口的Bean)对已经完成属性设置工作的Bean进行后续加工,直接装配出一个准备就绪的Bean。
小结一下:
- Spring IOC容器主要有继承体系底层的BeanFactory、高层的ApplicationContext和WebApplicationContext。
- Bean有自己的生命周期,容器加载Bean原理:
- BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中每一个解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中;
- 容器扫描BeanDefinitionRegistry中的BeanDefinition;调用InstantiationStrategy进行Bean实例化的工作;使用BeanWrapper完成Bean属性的设置工作;
- 单例Bean缓存池:Spring 在 DefaultSingletonBeanRegistry 类中提供了一个用于缓存单实例 Bean 的缓存器,它是一个用 HashMap 实现的缓存器,单实例的 Bean 以 beanName 为键保存在这个HashMap 中。
BeanFacotry vs FactoryBean
这个面试经常会问到,然后对这块我觉得也有点意思,就作为基础内容的一部分。
两者区别
BeanFactory是接口,提供了OC容器最基本的形式,给具体的IOC容器的实现提供了规范,
FactoryBean也是接口,为IOC容器中Bean的实现提供了更加灵活的方式,FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,我们可以在getObject()方法中灵活配置。其实在Spring源码中有很多FactoryBean的实现类。
区别:BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。
BeanFactory
回顾一下:BeanFactory,以Factory结尾,表示它是一个工厂类(接口), 它负责生产和管理bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。XmlBeanFactory类将持有此XML配置元数据,并用它来构建一个完全可配置的系统或应用。
更详细的内容,直接看文章的”BeanFactory“部分。
FactoryBean
一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。Spring自身就提供了70多个FactoryBean的实现,它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。
接口中定义的方法如下:
public interface FactoryBean<T> { /** * 返回对象的实例 */ T getObject() throws Exception; /** * 返回对象的类型 */ Class<?> getObjectType(); /** * 是否是单例 */ boolean isSingleton(); }
最好的学习就是动手实践一下,我们看一下spring如何通过Factorybean配置Bean,我们先定义一个car对象:
@Data public class Car { private int maxSpeed; private String brand; private double price; }
在定义一个CarFactoryBean,用来包装Car实例:
public class CarFactoryBean implements FactoryBean<Car> { private String carInfo ; /** * 自定义创建bean的过程,可以定制复杂的创建过程 */ @Override public Car getObject() throws Exception { Car car = new Car(); String[] infos = carInfo.split(","); car.setBrand(infos[0]); car.setMaxSpeed(Integer.valueOf(infos[1])); car.setPrice(Double.valueOf(infos[2])); return car; } /** * 获取FactoryBean创建bean的类型 */ @Override public Class<?> getObjectType() { return Car.class; } /** * 创建的bean,是否是单例 */ @Override public boolean isSingleton() { return false; } public String getCarInfo() { return carInfo; } /** * 接受逗号分隔符,设置属性 */ public void setCarInfo(String carInfo) { this.carInfo = carInfo; } }
这里是重点,需要添加如下配置:
<bean id="car" class="com.java.spring.factorybean2.CarFactoryBean"> <property name="carInfo" value="法拉利跑车,420,2600000"/> </bean>
最后测试用例如下:
public class MyFactoryBeanTest { /** * 测试验证FactoryBean原理,代理一个servcie在调用其方法的前后,打印日志亦可作其他处理 * 从ApplicationContext中获取自定义的FactoryBean * context.getBean(String beanName) ---> 最终获取到的Object是FactoryBean.getObejct(), * 使用Proxy.newInstance生成service的代理类 */ @Test public void testFactoryBean() { ApplicationContext context =new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); Car car = (Car) context.getBean("car"); System.out.println(car.toString()); } } // 输出: // Car(maxSpeed=420, brand=法拉利跑车, price=2600000.0)
测试的时候,我还特意打了个断点,确实会进入getObject()中:
网上本来找了2个示例,但是有一个一直跑步起来,然后这个示例,我找了N多个相关的博客,给出的示例都是缺胳膊少腿,完全不能运行,倒腾了大半个小时,终于倒腾出一个能运行的完整版本。
总结
对于IOC和DI理解,本文给出了2个比较经典的说明;对于BeanFactory和ApplicationContext的区别是使用方式,本文也从整体架构和使用姿势上,给了详细的说明;对于Bean生命周期和加载过程,这个应该是本文非常重要的内容,我觉得这块也是最有意思的。最后就是BeanFactory和FactoryBean,其实主要讲解FactoryBean,然后给了一个完整的示例。
这篇文章,除了示例部分是我写的,或者是我加工后的,八股文部分全部是摘抄自网络的博客,为了整理这篇比较全的IOC文章,也是看了大量的博客,之前本来打算买一本Spring相关的书籍来看的,因为时间原因,就还是找博客来学习吧,等我把Java生态的基础知识都学完后,我再重点学习更深入的知识,比如Spring框架源码。
可能有同学会说,你把网络上的整理资料汇总起来,有什么意义呢?其实这个对自己还是有很大帮助的,一方面你汇总这些资料,你其实需要看更多的资料,只有当你基本全部消耗完后,你才知道如何去整理相关文章,我之前没有写整理类的文章,这些博客看完后,过段时间就忘了。然后还有很重要的一点,整理文章,可以让你重新去熟悉这些知识,你能整理出来,证明你基本已经掌握了这些知识。因为这是你自己整理的知识,所以如果你想再捡起来,其实很很快,因为这篇文章是你概括的核心重点,如果你没有整理,等忘记是再重新去翻阅网络上的博客,你会被大量知识给淹没。
正所谓我不入地狱,谁如地狱,文章核心点都给大家整理好了,你如果想学习相关内容,整理好的资料你都不想看,你觉得你好意思么?
主要参考资料:
- Spring IOC知识点一网打尽:https://juejin.cn/post/6844903609067372558#heading-30
- Spring系列之beanFactory与ApplicationContext:https://www.cnblogs.com/xiaoxi/p/5846416.html
- Spring IOC原理总结:https://zhuanlan.zhihu.com/p/29344811
- 详解Spring IOC:https://www.jianshu.com/p/b60c30fdcc65 BeanFactory和FactoryBean区别:https://www.cnblogs.com/aspirant/p/9082858.html