• SpringBean的生命周期


    SpringBean的生命周期

    • SperingBean的生命周期是从Bean实例化之后,即通过反射创建出对象之后,到Bean成为一个完整对象,最终存储到单例池中,这个过程被称为Spring Bean的生命周期。Spring Bean的生命周期大体上分为三个阶段
      • Bean的实例化阶段:Spring框架会取出BeanDefiniton的信息进行判断当前Bean的范围是否是singleton的,是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的Bean通过反射(加载类)进行实例化
      • Bean的初始化阶段(着重研究):Bean创建之后还仅仅是个半成品,还需要对Bean实例的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法,执行InitializingBean接口的初始化方法、执行自定义初始化init方法等。该阶段是Spring最具有技术含量和复杂程度的阶段,AOP增强功能,后面学习的Spring的注解功能等、spring高频面试题Bean的循环引用问题都是在这个阶段体现的
      • Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池singletonObjects中,即完成了Spring Bean的整个生命周期。

    Spring Bean的初始化阶段涉及过程

    • Bean实例属性的填充
    • Aware接口属性的注入
    • BeanPostProcessor的before()方法回调
    • InitializingBean接口的初始化方法回调
    • 自定义初始化方法init回调
    • BeanPostProcessor的after()方法回调

    Bean实例属性填充

    • BeanDefinition中对当前Bean实体的注入信息通过属性propertyValue进行了储存,例如UserService的属性信息如下
        1. <bean id="userService" class="com.example.Service.Impl.UserServiceImpl">
        2. <property name="userDAO" ref="userDAO">property>
        3. <property name="name" value="hhhh">property>
        4. bean>
        5. <bean name="userDAO" class="com.example.DAO.Impl.UserDAOImpl">bean>
    属性注入的三种情况
    • 注入普通属性,String,int或存储基本类型的集合时,直接通过set方法反射设置进去
      • 上述属性name对应类中的set方法
    • 注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,再进行注入操作
      • 上述UserDAO属性的注入
      • 根据配置文件中bean的位置不同,进行的顺序也不一样
      • 主要代码如下
      • UserServiceImpl类
        1. package com.example.Service.Impl;
        2. import com.example.DAO.UserDAO;
        3. import com.example.Service.UserService;
        4. public class UserServiceImpl implements UserService {
        5. public UserServiceImpl() {
        6. System.out.println("UserService对象创建");
        7. }
        8. private UserDAO userDAO;
        9. private String name;
        10. public void setName(String name) {
        11. this.name = name;
        12. }
        13. public void setUserDAO(UserDAO userDAO) {
        14. System.out.println("UserService执行注入UserDAO的操作:setDAO方法");
        15. this.userDAO = userDAO;
        16. }
        17. @Override
        18. public void show() {
        19. System.out.println("show~~~");
        20. }
        21. }
      • UserDAOImpl类

        1. package com.example.DAO.Impl;
        2. import com.example.DAO.UserDAO;
        3. public class UserDAOImpl implements UserDAO {
        4. public UserDAOImpl() {
        5. System.out.println("UserDAO对象创建");
        6. }
        7. }
      • 测试类

        1. package com.example.Test;
        2. import com.example.Service.UserService;
        3. import org.springframework.context.support.ClassPathXmlApplicationContext;
        4. public class TestApplicationContext {
        5. public static void main(String[] args) {
        6. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        7. UserService UserServiceBean = (UserService) context.getBean(UserService.class);
        8. }
        9. }
      • 若配置文件中先创建UserADO的bean对象

        1. "1.0" encoding="UTF-8"?>
        2. <beans xmlns="http://www.springframework.org/schema/beans"
        3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        5. <bean class="com.example.PostProcessor.TimeLogBeanPostProcessor">bean>
        6. <bean name="userDAO" class="com.example.DAO.Impl.UserDAOImpl">bean>
        7. <bean id="userService" class="com.example.Service.Impl.UserServiceImpl">
        8. <property name="userDAO" ref="userDAO">property>
        9. <property name="name" value="hhhh">property>
        10. bean>
        11. beans>
      • 测试类运行结果为

      • 若配置文件中先创建UserService的bean对象

        1. "1.0" encoding="UTF-8"?>
        2. <beans xmlns="http://www.springframework.org/schema/beans"
        3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        5. <bean class="com.example.PostProcessor.TimeLogBeanPostProcessor">bean>
        6. <bean id="userService" class="com.example.Service.Impl.UserServiceImpl">
        7. <property name="userDAO" ref="userDAO">property>
        8. <property name="name" value="hhhh">property>
        9. bean>
        10. <bean name="userDAO" class="com.example.DAO.Impl.UserDAOImpl">bean>
        11. beans>
      • 运行结果如下

    • 注入双向对象引用属性时,就比较复杂了,涉及循环引用(循环依赖)问题,下面会详细阐释解决方案。
      • 循环依赖
        • 多个实体之间相互依赖并形成闭环的情况叫做“循环依赖”,也叫“循环引用”
        • 上述流程图显示如果按照常规的方法来存储bean对象,则就会陷入死循环,因为在bean对象的创建过程中,还需要进行初始化的过程,上述有讲到bean的生命周期, 当bean对象实例化完成之后,最终才会存放到单例池中,才完成交给Spring容器的功能。
      • Spring提供了三级缓存存储完整Bean实例半成品Bean实例,用于解决循环引用问题
      • 在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanResgisty中提供如下三个Map(分别存储不同状态下的Map)
          1. public class DefaultSingletonBeanRegistry .......{
          2. .....
          3. // todo 最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为’一级缓存‘
          4. private final Map singletonObjects=new ConcurrentHashMap(256);
          5. // todo 早期Bean单例池,缓存半成品对象,且当前对象已经被其它对象引用了,称之为‘二级缓存’
          6. private final Map earlySingletonObjects=new ConcurrentHashMap(16);
          7. // todo 单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时再通过工厂创建Bean,称之为‘三级缓存’
          8. private final Map>singletonFactories=new HashMap(16);
          9. .....
          10. }
        • 上述流程图中的实例化Service/DAO存放在三级缓存中,将其包装为对应的ObjectFactory,在ObjectFactory的getObject()方法中返回我们创建好的UserService/DAO,即上述流程图中的实例化Service/DAO,ObjectFactory是用来创建和管理Service/DAO对象的工厂类,而不是用来实例化Service/DAO对象的。被引用后,就会交给二级缓存,从三级换中移除。

     


    •  
    • 对于上述流程图的描述(UserService和userDAO循环依赖的过程结合上述三级缓存描述)
      • UserService实例化对象,但尚未完成初始化,将UserService存储到三级缓存中
      • UserService属性注入,需要USerDAO,从缓存中获取,没有USerDAO
      • USerDAO实例化对象,但尚未初始化,将UserDAO存储到三级缓存中
      • USerDAO属性注入,需要UserService,从三级缓存中获取USerService,UserService从三级缓存移入到二级缓存
      • UserDAO执行其它生命周期过程,最终成为一个完整的Bean,存储到一级缓存,删除二三级缓存
      • UserService注入UserDAO
      • UserService执行其它生命周期,最终成为一个完整的Bean,存储到一级缓存,删除二三级缓存

    常用的Aware接口

    •  Aware接口是一种框架辅助属性注入的一种思想,其它框架中也可以看见类似的接口,框架具有高度的封闭性,我们接触到的一般都是业务代码,一个底层的API代码不能轻易获取到,但这并不意味着永远用不到这些对象,如果用到了,就可以使用框架提供的类似Aware的接口,让框架给我们注入该对象
      • Aware接口回调方法作用
        ApplicationContextAwaresetApplicationContext(ApplicationContext applicationContext)允许Bean获取ApplicationContext对象,从而可以访问Spring容器的各种功能。
        BeanFactoryAwaresetBeanFactory(BeanFactory beanFactory)允许Bean获取BeanFactory对象,从而可以访问Spring容器的Bean工厂。
        BeanNameAwaresetBeanName(String name)允许Bean获取自己在Spring容器中的名称。
        ServletContextAwaresetServletContext(ServletContext servletContext)允许Bean获取ServletContext对象,从而可以访问Servlet容器的功能。

         
      • 在Spring中,Aware接口的作用是让Bean能够感知和获取一些特定的功能或资源。通过实现Aware接口,并在对应的回调方法中接收相关的对象,Bean可以在运行时获取到Spring容器提供的一些重要信息或功能。

        举个例子来说,如果一个Bean实现了ApplicationContextAware接口并实现了对应的回调方法,那么在Bean初始化的过程中,Spring容器会将ApplicationContext对象传递给该Bean。这样,该Bean就能够直接访问ApplicationContext,从而获取到Spring容器的各种功能,比如获取其他Bean、读取配置文件、发布事件等。这样的设计使得Bean能够更加灵活地与Spring容器进行交互,而不仅仅局限于自身的业务逻辑。

        类似地,其他的Aware接口也提供了类似的功能,让Bean能够获取到BeanFactory、Bean的名称、国际化消息、资源加载器、应用程序事件发布器、运行时环境等等。通过实现这些Aware接口,Bean可以获取到所需的功能或资源,从而实现更加灵活和可扩展的功能。

  • 相关阅读:
    二、准备开发与调试环境
    第七章(2):深度学习在自然语言处理NLP中的应用
    全球电梯空气消毒机行业调研及趋势分析报告
    聚观早报 | 苹果被曝开发16英寸iPad;5.5G已经取得关键进展
    [国外博士后申请]导师对简历的几点建议
    Linux——Shell脚本编程(1)
    My Thirty-Ninth Page - 二叉搜索树的最小绝对差 - By Nicolas
    【车间调度】基于模拟退火优化算法的的并行车间机器优化调度(Matlab代码实现)
    【洛谷 P2392】kkksc03考前临时抱佛脚 题解(动态规划+01背包)
    量化交易全流程(四)
  • 原文地址:https://blog.csdn.net/weixin_64939936/article/details/133210014