SpringBoot原理解析(二)- Spring Bean的生命周期以及后处理器和回调接口

SpringBoot原理解析(二)- Spring Bean的生命周期以及后处理器和回调接口

文章目录

Spring Bean的生命周期指从Bean的创建(实例化)、初始化,到使用(完成)和销毁的整个过程。

  • **Bean的实例化阶段:**Spring会取出BeanDefinition的信息进行判断当前Bean的范围是否是singleton的, 是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化;

  • Bean的初始化阶段:Bean创建之后还仅仅是个"半成品",还需要对Bean实例的属性进行填充、执行一些Aware 接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法 等。该阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能,相关注解功能等、 Bean的循环引用问题都是在这个阶段体现的;

  • **Bean的完成阶段:**经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池 singletonObjects中去了。

  • **Bean的销毁阶段:**当Bean不再使用时,Spring会调用Bean的销毁方法进行清理工作。

1.Bean的实例化阶段

1.1.Bean 实例化的基本流程

Spring容器在进行初始化时,会将xml配置的的信息封装成一个BeanDefinition对象(详细请见《SpringBoot原理解析(一)- 基于xml配置bean(Java解析xml文件)》),所有的 BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去,Spring框架在对该Map进行遍历,使用反射创建Bean实例对象,创建好的Bean对象存储在一个名为singletonObjects的Map集合中,当调用getBean方法 时则最终从该Map集合中取出Bean实例对象返回。

步骤:

  • 加载xml配置文件,解析获取配置中的每个的信息,封装成一个个的BeanDefinition对象;

  • 将BeanDefinition存储在一个名为beanDefinitionMap的Map中;

  • ApplicationContext底层遍历beanDefinitionMap,创建Bean实例对象;

  • 创建好的Bean实例对象,被存储到一个名为singletonObjects的Map中;

  • 当执行applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回

1.2.Bean 实例化图例

1.3.实例化阶段的后处理器

Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册 BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。

  • **BeanFactoryPostProcessor:**Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;

  • **BeanDefinitionRegistryPostProcessor:**BeanDefinition注册后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行singletonObjects之前执行。

1.3.1.实例化阶段后处理器的介入流程图
1.3.2.BeanDefinitionRegistryPostProcessor

在Spring容器初始化过程中,首先会加载Bean定义,然后通过注册机制将这些Bean定义注册到容器中。在Bean定义注册的过程中,如果我们需要对Bean定义进行修改或添加一些自定义的处理逻辑,可以实现BeanDefinitionRegistryPostProcessor接口,并实现其中的postProcessBeanDefinitionRegistry 方法。

java 复制代码
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

   /**
    * 在所有的Bean定义加载完毕后,但在Bean的实例化和初始化之前执行
    * 注意:该处理器方法优先于postProcessBeanFactory
    */
   void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}
1.3.3.BeanFactoryPostProcessor

在Spring容器初始化过程中,当所有的Bean定义加载完成后,但在Bean的实例化和初始化之前,会调用所有实现了BeanFactoryPostProcessor接口的类的postProcessBeanFactory方法。

java 复制代码
public interface BeanFactoryPostProcessor {

   /**
    * 在所有的Bean定义加载完毕后,但在Bean的实例化和初始化之前执行
    */
   void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

2.Bean的初始化阶段

2.1.主要流程

Spring Bean的初始化过程涉及如下几个过程,执行优先级从上到下:

  1. org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation

  2. Bean的构造器方法

  3. org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation

  4. org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessProperties

  5. Bean属性填充

    Bean实例属性填充 Spring在进行属性注入时,会分为如下几种情况:

    • 注入普通属性,String、Integer或存储基本类型的集合时,直接通过set方法的反射设置进去;

    • 注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被 注入对象Bean实例(完成整个生命周期)后,在进行注入操作;

    • 注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)问题。

  6. org.springframework.beans.factory.BeanNameAware#setBeanName

  7. org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader

  8. org.springframework.beans.factory.BeanFactoryAware#setBeanFactory

  9. org.springframework.context.ApplicationContextAware#setApplicationContext

  10. org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization

  11. @postConstruct

  12. org.springframework.beans.factory.InitializingBean#afterPropertiesSet

  13. @Bean注解解析的初始化方法initMethod()

  14. org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization

2.2.初始化流程图

2.3.循环依赖

2.3.1.问题描述
xml 复制代码
<!-- userService依赖了orderService -->
<bean id="userService" class="org.ahao.demo.service.UserService">
     <property name="orderService" re="orderService"/>
</bean>

<!-- orderService依赖了userService -->
<bean id="orderService" class="org.ahao.demo.service.OrderService">
     <property name="userService" re="userService"/>
</bean>
2.3.2.三级缓存

通过使用三级缓存,Spring可以提高Bean实例的获取效率和避免循环引用带来的问题。

java 复制代码
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
  
  /** Cache of singleton objects: bean name to bean instance. */
  // "第一级缓存":实例化和初始化都完成了的完整Bean
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of early singleton objects: bean name to bean instance. */
  // "第二级缓存":半成品Bean,并且该Bean已经被其他的Bean对象引用了
	private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
  
  /** Cache of singleton factories: bean name to ObjectFactory. */
  // "第三级缓存":半成品Bean,该Bean未被其他的Bean对象引用
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  
}

大致流程如下:

  • UserService 实例化对象,但尚未初始化,将UserService存储到第三级缓存singletonFactories;
  • UserService 属性注入,需要OrderService,从缓存中获取,没有OrderService;
  • OrderService实例化对象,但尚未初始化,将OrderService存储到到第三级缓存singletonFactories;
  • OrderService属性注入,需要UserService,从三级缓存获取UserService,UserService从第三级缓存singletonFactories移入第二级缓存earlySingletonObjects;
  • OrderService执行其他生命周期过程,最终成为一个完成Bean,存储到第一级缓存singletonObjects,删除第二三级缓存;
  • UserService 注入OrderService;
  • UserService执行其他生命周期过程,最终成为一个完成Bean,存储到第一级缓存singletonObjects,删除二三级缓存。
2.3.3.三级缓存流程图

3.Bean的完成阶段

在初始化后,Bean可以被容器和其他Bean使用。此时,Bean处于活动状态,并可以响应外部请求。

java 复制代码
@Service
public class OrderServiceImpl implement OrderService {

   @Autowired
   // 注入bean
   private UserService userService;
  
	 @Override
   public User getOrderUser(long orderId){
     // 根据订单id获取uid
     String uid = ...;
     // 使用Bean(已完成初始化,成为一个完整的Bean)
     return userService.getUserById(uid);
   }
  
}

4.Bean的销毁阶段

当Bean不再使用时,Spring会调用Bean的销毁方法进行清理工作。可以通过实现DisposableBean接口并实现destroy方法,或使用@PreDestroy注解定义销毁方法。

  • @PreDestroy
  • org.springframework.beans.factory.DisposableBean#destroy
相关推荐
TDengine (老段)7 小时前
TDengine 数学函数 CRC32 用户手册
java·大数据·数据库·sql·时序数据库·tdengine·1024程序员节
心随雨下7 小时前
Tomcat日志配置与优化指南
java·服务器·tomcat
Kapaseker7 小时前
Java 25 中值得关注的新特性
java
wljt7 小时前
Linux 常用命令速查手册(Java开发版)
java·linux·python
撩得Android一次心动7 小时前
Android 四大组件——BroadcastReceiver(广播)
android·java·android 四大组件
canonical_entropy7 小时前
Nop平台到底有什么独特之处,它能用在什么场景?
java·后端·领域驱动设计
chilavert3187 小时前
技术演进中的开发沉思-174 java-EJB:分布式通信
java·分布式
不是株8 小时前
JavaWeb(后端进阶)
java·开发语言·后端
编程火箭车8 小时前
【Java SE 基础学习打卡】02 计算机硬件与软件
java·电脑选购·计算机基础·编程入门·计算机硬件·软件系统·编程学习路线