spring 作为目前我们开发的基础框架, 每天的开发工作基本和他形影不离, 作为管理 bean 的最经典, 优秀的框架, 它的复杂程度往往令人望而却步. 不过作为朝夕相处的框架, 我们必须得明白一个问题就是 spring 是如何加载 bean 的, 我们常在开发中使用的注解比如 @Component,@AutoWired,@Socpe 等注解, Spring 是如何解析的, 明白这些原理将有助于我们更深刻的理解 spring. 需要说明一点的是 spring 的源码非常精密, 复杂, 限于篇幅的关系, 本篇博客不会细致的分析源码, 会采取抽丝剥茧的方式, 避轻就重, 抓住重点来分析整个流程(不会分析具体的细节), 本次将会基于 spring5.0 的版本
本篇博客的目录:
一: spring 读取配置或注解的过程
二: spring 的 bean 的生命周期
三: spring 的 BeanPostProcessor 处理器
四: 一些关键性的问题
五: 测试
六: 总结
一: spring 读取配置或注解的过程
1: 先通过扫描指定包路径下的 spring 注解, 比如 @Component,@Service,@Lazy @Sope 等 spring 识别的注解或者是 xml 配置的属性 (通过读取流, 解析成 Document,Document) 然后 spring 会解析这些属性, 将这些属性封装到 BeanDefintaion 这个接口的实现类中.
在 springboot 中, 我们也可以采用注解配置的方式:
比如这个配置 Bean,spring 也会将 className,scope,lazy 等这些属性装配到 PersonAction 对应的 BeanDefintaion 中. 具体采用的是 BeanDefinitionParser 接口中的 parse(Element element, ParserContext parserContext)方法, 该接口有很多不同的实现类. 通过实现类去解析注解或者 xml 然后放到 BeanDefination 中, BeanDefintaion 的作用是集成了我们的配置对象中的各种属性, 重要的有这个 bean 的 ClassName, 还有是否是 Singleton, 对象的属性和值等(如果是单例的话, 后面会将这个单例对象放入到 spring 的单例池中).spring 后期如果需要这些属性就会直接从它中获取. 然后, 再注册到一个 ConcurrentHashMap 中, 在 spring 中具体的方法就是 registerBeanDefinition(), 这个 Map 存的 key 是对象的名字, 比如 Person 这个对象, 它的名字就是 person, 值是 BeanDefination, 它位于 DefaultListableBeanFactory 类下面的 beanDefinitionMap 类属性中, 同时将所有的 bean 的名字放入到 beanDefinitionNames 这个 list 中, 目的就是方便取 beanName;
二: spring 的 bean 的生命周期
spring 的 bean 生命周期其实最核心的分为 4 个步骤, 只要理清三个关键的步骤, 其他的只是在这三个细节中添加不同的细节实现, 也就是 spring 的 bean 生明周期:
实例化和初始化的区别: 实例化是在 jvm 的堆中创建了这个对象实例, 此时它只是一个空的对象, 所有的属性为 null. 而初始化的过程就是讲对象依赖的一些属性进行赋值之后, 调用某些方法来开启一些默认加载. 比如 spring 中配置的数据库属性 Bean, 在初始化的时候就会将这些属性填充, 比如 driver,jdbcurl 等, 然后初始化连接
2.1: 实例化 Instantiation
AbstractAutowireCapableBeanFactory.doCreateBean 中会调用 createBeanInstance()方法, 该阶段主要是从 beanDefinitionMap 循环读取 bean, 获取它的属性, 然后利用反射 (core 包下有 ReflectionUtil 会先强行将构造方法 setAccessible(true)) 读取对象的构造方法(spring 会自动判断是否是有参数还是无参数, 以及构造方法中的参数是否可用), 然后再去创建实例(newInstance)
2.2: 初始化
初始化主要包括两个步骤, 一个是属性填充, 另一个就是具体的初始化过程
2.2.1: 属性赋值 PopulateBean()会对 bean 的依赖属性进行填充,@AutoWired 注解注入的属性就发生这个阶段, 假如我们的 bean 有很多依赖的对象, 那么 spring 会依次调用这些依赖的对象进行实例化, 注意这里可能会有循环依赖的问题. 后面我们会讲到 spring 是如何解决循环依赖的问题
2.2: 初始化 Initialization
初始化的过程包括将初始化好的 bean 放入到 spring 的缓存中, 填充我们预设的属性进一步做后置处理等
3: 使用和销毁 Destruction
在 Spring 将所有的 bean 都初始化好之后, 我们的业务系统就可以调用了. 而销毁主要的操作是销毁 bean, 主要是伴随着 spring 容器的关闭, 此时会将 spring 的 bean 移除容器之中. 此后 spring 的生命周期到这一步彻底结束, 不再接受 spring 的管理和约束.
三: spring 的 BeanPostProcessor 处理器
spring 的另一个强大之处就是允许开发者自定义扩展 bean 的初始化过程, 最主要的实现思路就是通过 BeanPostProcessor 来实现的, spring 有各种前置和后置处理器, 这些处理器渗透在 bean 创建的前前后后, 穿插在 spring 生命周期的各个阶段, 每一步都会影响着 spring 的 bean 加载过程. 接下来我们就来分析具体的过程:
3.1: 实例化阶段
该阶段会调用对象的空构造方法进行对象的实例化, 在进行实例化之后, 会调用 InstantiationAwareBeanPostProcessor 的 postProcessBeforeInstantiation 方法
BeanPostProcessor(具体实现是 InstantiationAwareBeanPostProcessor). postProcessBeforeInstantiation();
这个阶段允许在 Bena 进行实例化之前, 允许开发者自定义逻辑, 如返回一个代理对象. 不过需要注意的是假如在这个阶段返回了一个不为 null 的实例, spring 就会中断后续的过程.
BeanPostProcessor.postProcessAfterInstantiation();
这个阶段是 Bean 实例化完毕后执行的后处理操作, 所有在初始化逻辑, 装配逻辑之前执行
3.2: 初始化阶段
3.2.1:BeanPostProcessor.postProcessBeforeInitialization
该方法在 bean 初始化方法前被调用, Spring AOP 的底层处理也是通过实现 BeanPostProcessor 来执行代理逻辑的
3.2.2:InitializingBean.afterPropertiesSet
自定义属性值 该方法允许我们进行对对象中的属性进行设置, 假如在某些业务中, 一个对象的某些属性为 null, 但是不能显示为 null, 比如显示 0 或者其他的固定数值, 我们就可以在这个方法实现中将 null 值转换为特定的值
3.2.3:BeanPostProcessor.postProcessAfterInitialization(Object bean, String beanName). 可以在这个方法中进行 bean 的实例化之后的处理, 比如我们的自定义注解, 对依赖对象的版本控制自动路由切换. 比如有一个服务依赖了两种版本的实现, 我们如何实现自动切换呢? 这时候可以自定义一个路由注解, 假如叫 @RouteAnnotaion, 然后实现 BeanPostProcessor 接口, 在其中通过反射拿到自定义的注解 @RouteAnnotaion 再进行路由规则的设定.
3.2.4:SmartInitializingSingleton.afterSingletonsInstantiated
4.1: 容器启动运行阶段
4.1.1:SmartLifecycle.start
容器正式渲染完毕, 开始启动阶段, bean 已经在 spring 容器的管理下, 程序可以随时调用
5.1: 容器停止销毁
5.1.1:SmartLifecycle.stop(Runnable callback)
spring 容器停止运行
5.1.2:DisposableBean.destroy()
spring 会将所有的 bean 销毁, 实现的 bean 实例被销毁的时候释放资源被调用
四: 一些关键性的问题
4.1:FactoryBean 和 BeanFactory 的区别?
BeanFactory 是个 bean 工厂类接口, 是负责生产和管理 bean 的工厂, 是 IoC 容器最底层和基础的接口, spring 用它来管理和装配普通 bean 的 IoC 容器, 它有多种实现, 比如 AnnotationConfigApplicationContext,XmlwebApplicationContext 等.
FactoryBean 是 FactoryBean 属于 spring 的一个 bean, 在 IoC 容器的基础上给 Bean 的实现加上了一个简单工厂模式和装饰模式, 是一个可以生产对象和装饰对象的工厂 bean, 由 spring 管理, 生产的对象是由 getObject()方法决定的. 注意: 它是泛型的, 只能固定生产某一类对象, 而不像 BeanFactory 那样可以生产多种类型的 Bean. 在对于某些特殊的 Bean 的处理中, 比如 Bean 本身就是一个工厂, 那么在其进行单独的实例化操作逻辑中, 可能我们并不想走 spring 的那一套逻辑, 此时就可以实现 FactoryBean 接口自己控制逻辑.
4.2:spring 如何解决循环依赖问题
循环依赖问题就是 A->B->A,spring 在创建 A 的时候, 发现需要依赖 B, 因为去创建 B 实例, 发现 B 又依赖于 A, 又去创建 A, 因为形成一个闭环, 无法停止下来就可能会导致 CPU 计算飙升
如何解决这个问题呢? spring 解决这个问题主要靠巧妙的三层缓存, 所谓的缓存主要是指这三个 map,singletonObjects 主要存放的是单例对象, 属于第一级缓存; singletonFactories 属于单例工厂对象, 属于第二级缓存; earlySingletonObjects 属于第二级缓存, 如何理解 early 这个标识呢? 它表示只是经过了实例化尚未初始化的对象. Spring 首先从 singletonObjects(一级缓存)中尝试获取, 如果获取不到并且对象在创建中, 则尝试从 earlySingletonObjects(二级缓存)中获取, 如果还是获取不到并且允许从 singletonFactories 通过 getObject 获取, 则通过 singletonFactory.getObject()(三级缓存)获取. 如果获取到了则则移除对应的 singletonFactory, 将 singletonObject 放入到 earlySingletonObjects, 其实就是将三级缓存提升到二级缓存, 这个就是缓存升级. spring 在进行对象创建的时候, 会依次从一级, 二级, 三级缓存中寻找对象, 如果找到直接返回. 由于是初次创建, 只能从第三级缓存中找到(实例化阶段放入进去的), 创建完实例, 然后将缓存放到第一级缓存中. 下次循环依赖的再直接从一级缓存中就可以拿到实例对象了.
五: 测试
我们来写一个测试类, 验证一下上面的问题:
5.1: 首先声明一个自定义的 Bean
- @Component
- public class CustomBean {
- public CustomBean(){
- System.out.println("调用 CustomBean 空的构造方法");
- }
- }
5.2: 声明一个 Bean 来实现 BeanPostProcessor
- package com.wyq.spring.bean;
- import org.springframework.beans.BeansException;
- import org.springframework.beans.PropertyValues;
- import org.springframework.beans.factory.DisposableBean;
- import org.springframework.beans.factory.SmartInitializingSingleton;
- import org.springframework.beans.factory.config.BeanPostProcessor;
- import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
- import org.springframework.context.annotation.Scope;
- import org.springframework.stereotype.Component;
- import java.beans.PropertyDescriptor;
- /**
- * @Author: wyq
- * @Desc:
- * @Date: 2019/9/1 15:36
- **/
- @Component
- @Scope("singleton")
- public class TestBean implements BeanPostProcessor, SmartInitializingSingleton, InstantiationAwareBeanPostProcessor, DisposableBean{
- private static final String BEAN_NAME= "customBean";
- @Override
- public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
- if (BEAN_NAME.equals(beanName)) {
- System.out.println("==>BeanPostProcessor.postProcessBeforeInitialization");
- }
- return null;
- }
- @Override
- public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
- if (BEAN_NAME.equals(beanName)) {
- System.out.println("==>BeanPostProcessor.postProcessAfterInitialization");
- }
- return null;
- }
- @Override
- public void afterSingletonsInstantiated() {
- System.out.println("==>SmartInitializingSingleton.afterSingletonsInstantiated");
- }
- @Override
- public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
- if (BEAN_NAME.equals(beanName)) {
- System.out.println("==>InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation");
- }
- return null;
- }
- @Override
- public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
- if (BEAN_NAME.equals(beanName)) {
- System.out.println("==>InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation");
- }
- return false;
- }
- @Override
- public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
- System.out.println("==>InstantiationAwareBeanPostProcessor.postProcessPropertyValues");
- return null;
- }
- @Override
- public void destroy() throws Exception {
- System.out.println("==>DisposableBean.destroy");
- }
- }
5.3: 启动容器:
六: 总结
本篇博客主要是介绍了 Spring 的一些实例化的过程, 高屋建瓴的分析了一下 spring 的 bean 加载过程, 没有详细展开某个细节分析. spring 的内部源码非常复杂, 每个接口的实现类都在 5 个以上, 如果深入细节, 恐怕不是一篇博客能讲清楚的. 这篇博客的目的就是在阐述 spring 的基本脉络中心路线顺序, 首先我们需要有一个总体的认识, 然后再深入到细节就是轻而易举的了. 这也是一种学习的方法论, 通过本篇博客我希望能梳理清楚 spring 的基本流程, 对 spring 有一个比较清晰的认识. 并且学习到优秀开源框架的设计基本思想, 还有就是进一步提升自己的阅读源码的能力.
来源: https://www.cnblogs.com/wyq178/p/11415877.html