首页 > 其他分享 >Spring中Bean的“一生”(生命周期)

Spring中Bean的“一生”(生命周期)

时间:2024-08-18 21:56:22浏览次数:15  
标签:初始化 生命周期 Spring 接口 Bean 实例 方法

目录

一、Bean 实例化的基本流程

1、Bean 实例化的基本流程

2、Bean信息定义对象-BeanDefinition

3、Bean 实例化的基本流程 

二、Spring的后处理器

2、BeanPostProcessor

三、Spring Bean的生命周期

1、什么是SpringBean的生命周期

2、SpringBean的初始化过程 

3、SpringBean的生命周期阶段:

Spring的声明周期总体分为四个阶段:实例化=>属性注入=>初始化=>销毁。​编辑

3、Bean实例属性填充

四、常用的Aware接口

五、生命周期总结 


一、Bean 实例化的基本流程

1、Bean 实例化的基本流程

        Spring容器在进行初始化时,会将xml配置的的信息封装成一个BeanDefinition对象,所有的 BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去,Spring框架再对该Map进行遍 历,使用反射创建Bean实例对象,创建好的Bean对象存储在一个名为singletonObjects的Map集合中,当调用 getBean方法 时则最终从该Map集合中取出Bean实例对象返回。

2、Bean信息定义对象-BeanDefinition

BeanDefinition存储到一个名为beanDefinitionMap

 

对象存储在一个名为singletonObjects的Map

 

3、Bean 实例化的基本流程 
  • 加载xml配置文件,解析获取配置中的每个的信息封装成一个个的BeanDefinition对象;
  • 将BeanDefinition存储在一个名为beanDefinitionMap的Map中;
  • ApplicationContext底层遍历beanDefinitionMap,创建Bean实例对象;
  • 创建好的Bean实例对象,被存储到一个名为singletonObjects的Map中;
  • 当执行applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回

 

二、Spring的后处理器

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

  • BeanFactoryPostProcessor:Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例 化之前执行;
  • BeanPostProcessor:Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects 之前执行。

1、BeanFactoryPostProcessor
(1)Bean工厂后处理器 – BeanFactoryPostProcessor
        BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。

        演示修改:

  • 创建BeanFactoryPostProcessor实现类并重写方法
  • 注入实现类

 

实例代码(1):

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
 
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("MyBeanFactoryPostProcessor的postProcessBeanFactory");
//        修改演示
//        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("user");
//        beanDefinition.setBeanClassName("com.apesource.pojo.Student");
 
        // 注册演示
        BeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClassName("com.apesource.pojo.Student");
//        强转成DefaultListableBeanFactory
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
        defaultListableBeanFactory.registerBeanDefinition("stu",beanDefinition);
    }
}

 这个MyBeanFactoryPostProcessor类实现了BeanFactoryPostProcessor接口,允许在Spring容器实例化任何Bean之前,对应用上下文中的Bean定义进行自定义修改。

        postProcessBeanFactory这是一个主要方法,其中进行Bean定义的自定义处理,Spring容器在加载完所有Bean定义后但在实例化任何Bean之前会调用这个方法。

        ConfigurableListableBeanFactory代表可配置的 BeanFactory 实例,可以用来获取和修改 BeanDefinition。

        首先是修饰演示,获取到已有的BeanDefinition并对其修改配置,这里是获取了id为user的bean并将bean的实现类改为com.apesource.pojo.Student,从而实现了修改。

        其次是注册演示,创建一个BeanDefinition对象,对这个对象使用set方法将bean的实现类设置为com.apesource.pojo.Student。然后把当前的ConfigurableListableBeanFactory创建的对象强为DefaultListableBeanFactory这是 Spring 中一种常见的 BeanFactory 实现类,它允许进行 Bean 的注册和删除。再调用DefaultListableBeanFactory的registerBeanDefinition方法,将刚才的BeanDefinition对象注册到 BeanFactory 中,并起名为stu。

        applicationContext.xml中配置:

<bean class="com.apesource.processor.MyBeanFactoryPostProcessor"></bean>

测试代码

public class Test01 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Object user = applicationContext.getBean("user");
        System.out.println(user);
    }
}

修饰演示的代码结果为:com.apesource.pojo.Student

(2)Bean工厂后处理器 – BeanDefinitionRegistryPostProcessor
        Spring 提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition操作。

        实例代码(2):

public class MyBeanFactoryPostProcessor2 implements BeanDefinitionRegistryPostProcessor {
 
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        BeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClassName("com.apesource.pojo.Student");
        beanDefinitionRegistry.registerBeanDefinition("stu2",beanDefinition);
    }
 
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
 
    }
}

这里首先是继承了BeanDefinitionRegistryPostProcessor接口,那么我们就可以进行修改 BeanFactory 的配置,还可以直接注册新的 Bean 定义。

        重写了postProcessBeanDefinitionRegistry方法,该方法在 Spring 容器初始化期间调用,并传递一个 BeanDefinitionRegistry 实例,允许直接操作 Bean 定义注册表

        首先先创建了一个RootBeanDefinition对象,用来描述一个新的 Bean。然后向该对象设置这个bean的实现类为com.apesource.pojo.Student,最后使用registerBeanDefinition方法注册一个实现类为com.apesource.pojo.Student名叫stu的bean。

        它和上面的BeanFactoryPostProcessor的区别就是BeanDefinitionRegistryPostProcessor可以直接注册新的bean,而BeanFactoryPostProcessor需要强制转换后才能注册新的bean。

        applicationContext.xml中配置:

<bean class="com.apesource.processor.MyBeanFactoryPostProcessor2"></bean>

测试代码:

public class Test01 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Object stu = applicationContext.getBean("stu2");
        System.out.println(stu);
    }
}

         测试结果:com.apesource.pojo.Student@3108bc

2、BeanPostProcessor

Bean后处理器 – BeanPostProcessor

        Bean被实例化后,到最终缓存到名为singletonObjects单例池之前中间会经过Bean的初始化过程, 例如:属性的填充、初始方法init的执行等,其中有一个对外进行扩展的点BeanPostProcessor,我们称为Bean后处理。跟上面的 Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor, 会在流程节点上被Spring自动调用。

实例代码

// 接口允许开发者在 Spring 容器的 Bean 初始化前后,插入自定义的逻辑。
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcessor的before方法...");
        return bean;
    }
 
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcessor的after方法...");
        return bean;
    }
}

  首先这个类实现了BeanPostProcessor接口,呢我们就可以在 Spring 容器中的 Bean 初始化之前和之后执行自定义逻辑

        第一个重写的方法postProcessBeforeInitialization这个方法会在 Bean 初始化方法(如 @PostConstruct 注解的方法或自定义的初始化方法)调用之前执行。两个参数分别为当前正在处理的 Bean 实例、当前 Bean 的名称。在处理完逻辑后,必须返回一个 Bean 对象。这里返回的是传入的原始 Bean,没有进行任何修改。

        第二个重写的方法是postProcessAfterInitialization这个方法会在 Bean 初始化方法调用之后执行。参数和上面的方法参数一致。同样返回传入的原始 Bean,没有进行任何修改。

        applicationContext.xml中配置:

<bean class="com.apesource.processor.MyBeanPostProcessor"></bean>

我们在刚才的代码进行优化,对实体类User进行构造方法等方法的写入

public class User implements InitializingBean {
    public User() {
        System.out.println("===》无参构造方法S");
    }
 
    private String uname;
 
    public void setUname(String uname) {
        this.uname = uname;
        System.out.println("===》执行set方法");
    }
 
    @Override
    public void afterPropertiesSet() throws Exception {
        // 这个接口定义了一个回调方法 afterPropertiesSet()。当 Spring 完成所有属性的注入后,容器会调用这个方法,用于进行一些初始化工作。
        System.out.println("===》接口初始化");
    }
 
    public void doinit(){
        System.out.println("===》属性初始化");
    }
}

其中实现了了InitializingBean接口用于重写afterPropertiesSet()方法,该方法会在所有属性(包括 uname)被注入后,由 Spring 容器自动调用,或者说是在 Bean 初始化阶段被调用

        doinit方法是自定义的初始化方法,可以通过 Spring 的 XML 配置或注解配置来调用,这个方法在 Spring 容器初始化完成后可以被显式调用,用于执行一些额外的初始化逻辑。

        那么我们在执行的时候顺序就为:

 

        首先是无参构造->执行set方法->然后是BeanPostProcessor的before方法->执行接口初始化->执行属性初始化->BeanPostProcessor的after方法->最后实例化成功,返回user对象地址。 

三、Spring Bean的生命周期

1、什么是SpringBean的生命周期

        Spring Bean的生命周期是从 Bean 实例化之后,即通过反射创建出对象之后,到Bean成为一个完整对象,最终存储到单例池中,这个过程被称为Spring Bean的生命周期。Spring Bean的生命周期大体上分为三个阶段:

Bean的实例化阶段:Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是 singleton的, 是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的 Bean通过反射进行实例化;
Bean的初始化阶段:Bean创建之后还仅仅是个"半成品",还需要对Bean实例的属性进行填充、执 行一些Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段;
Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池 singletonObjects中去了,即完成了Spring Bean的整个生命周期。

2、SpringBean的初始化过程 

        由于Bean的初始化阶段的步骤比较复杂,所以着重研究Bean的初始化阶段,Spring Bean的初始化过程涉及如下几个过程:

  • Bean实例的属性填充
  • Aware接口属性注入
  • BeanPostProcessor的before()方法回调
  • InitializingBean接口的初始化方法回调
  • 自定义初始化方法init回调
  • BeanPostProcessor的after()方法回调
3、SpringBean的生命周期阶段:
Spring的声明周期总体分为四个阶段:实例化=>属性注入=>初始化=>销毁。
  • Spring Bean 的生命周期总体分四个阶段:实例化=>属性注入=>初始化=>销毁
  • Step1实例化Bean:根据配置文件中Bean 的定义,利用Java Reflection反射技术创建Bean的实例
  • Step2注入对象依赖的属性值(或对象)
  • Step3处理各种Aware接口:Spring 会检测该Bean是否实现了xxxAware接口,通过(Aware类型的接口,可以让Spring框架为当前Bean注入相应的内容。
    • 如果Bean实现 BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,注入Bean的名字;
    • 如果 Bean实现 BeanclassLoaderAware接口,调用setBeanClassLoader()方法,注入classLoader对象的实例;
    • 如果Bean实现BeanFactoryAware 接口,会调用它实现的setBeanFactory()方法,注入的是Spring工厂;
    • 如果 Bean实现 ApplicationContextAware接口,会调用setApplicationContext()方法,注入Spring上下文;
  • Step4执行BeanPostProcessor前置处理:如果想对Bean进行一些自定义的前置处理,那么可以让Bean 实现了(BeanPostProcessor接口,将会在该阶段调用postProcessBeforeInitialization(Object obj,string s)方法。
  • Step5执行InitializingBean初始化方法:如果Bean 实现了InitializingBean接口,执行afeterPropertiesSet()方法。
  • Step6执行init-method自定义初始化方法:如果Bean 在 Spring 配置文件中配置了init-method属性,则会自动调用其配置的初始化方法。
  • .Step7执行BeanPostProcessor后置处理:如果这个 Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(0bject obj,string s)方法,由于这个方法是在 Bean初始化结束后调用;
  • 以上几个步骤完成后,|Bean已经被正确创建,可以正常使用这个Bean
  • Step8执行DisposableBean销毁Bean:当Bean 不再需要时,会经过清理阶段,如果Bean 实现了DisposableBean这个接口,会调用其实现的destroy()方法执行销毁;
  • Step9执行destroy-method自定义销毁方法:如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的自定义销毁方法。
3、Bean实例属性填充

        BeanDefinition 中有对当前Bean实体的注入信息通过属性propertyValues进行了存储,例如:

总结:Spring在进行属性注入时,会分为如下几种情况:

  • 注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;
  • 注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作;
  • 注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)

   多例含义:多个实体之间相互依赖并形成闭环的情况就叫做"循环依赖",也叫做"循环引用";

public class DefaultSingletonBeanRegistry ... {
 //1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"
 Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
 //2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存"
 Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
 //3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三
级缓存"
 Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}
注:将对象保存至三级缓存的时候,会包装成ObjectFactory对象录入,未来通过此接口对应的get方法再
次提取对象

 

解决方案

        Spring提供了三级缓存存储完整Bean实例和半成品Bean实例,用于解决循环引用问题在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:

public class DefaultSingletonBeanRegistry ... {
 //1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"
 Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
 //2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存"
 Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
 //3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三
级缓存"
 Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}
注:将对象保存至三级缓存的时候,会包装成ObjectFactory对象录入,未来通过此接口对应的get方法再
次提取对象

 UserService和UserDao循环依赖的过程结合上述三级缓存描述一下:

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

一级缓存:存储的是完整的、初始化后的单例(Singleton)Bean 实例。

二级缓存:存储的是早期的 Bean 实例,通常是已经实例化但还未进行完全初始化(如未完成属性填充)的 Bean。

三级缓存:存储的是一个 Bean 的工厂对象,用于创建半成品的 Bean 实例(通常是代理对象)。

四、常用的Aware接口

        Aware接口是一种框架辅助属性注入的一种思想,其他框架中也可以看到类似的接口。框架具备高度封装性,我们接触到的一般都是业务代码,一个底层功能API不能轻易的获取到,但是这不意味着永远用不到这些对象,如果用到了,就可以使用框架提供的类似Aware的接口,让框架给我们注入该对象。

处理器的作用,为Bean生命周期各个阶段提供扩展。

        实例代码:

public class User implements BeanNameAware, ApplicationContextAware, BeanFactoryAware {
 
    // BeanFactoryAware接口
    // 当 Spring 容器创建 User Bean 时,会调用这个方法,并传递当前的 BeanFactory 实例作为参数。控制台会输出这个 BeanFactory 的信息。
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println(beanFactory);
    }
 
    // BeanNameAware
    // 当 Spring 创建并配置 User Bean 时,会调用这个方法,并将 Bean 的名称作为参数传递给它。控制台会输出该名称,即 "s"。
    @Override
    public void setBeanName(String s) {
        System.out.println(s);
    }
 
    // ApplicationContextAware
    // 当 Spring 容器创建 User Bean 时,会调用这个方法,并传递当前的 ApplicationContext 实例作为参数。控制台会输出这个 ApplicationContext 的信息。
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println(applicationContext);
    }
 
    // 顺序setBeanName setBeanFactory setApplicationContext
}

这个类实现了BeanNameAware、ApplicationContextAware和BeanFactoryAware它们提供了一些回调方法,使 Bean 可以在初始化过程中获取 Spring 容器的相关信息。

        setBeanFactory方法是当 Spring 容器创建 User Bean 时,会调用此方法,并将当前的 BeanFactory 实例作为参数传递给它。

        setBeanName方法是当 Spring 容器创建并配置 User Bean 时,会调用此方法,并将 Bean 的名称作为参数传递给它。

        setApplicationContext方法是当 Spring 容器创建 User Bean 时,会调用此方法,并将当前的 ApplicationContext 实例作为参数传递给它。

        三者的方法调用顺序为:setBeanName(先设置 Bean 的名称)-> setBeanFactory(然后传递 BeanFactory 实例)-> setApplicationContext(最后传递 ApplicationContext 实例)。

        测试代码:

public class Test01 {
 
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Object user = applicationContext.getBean("users");
        System.out.println(user);
    }
 
}

结果为:

五、生命周期总结 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

标签:初始化,生命周期,Spring,接口,Bean,实例,方法
From: https://blog.csdn.net/m0_68041576/article/details/141305203

相关文章

  • (附论文)基于Springboot和Vue的二手车交易系统(172)
    获取源码请滑到最底部访问官网项目配套调试视频和相对应的软件安装包1、项目描述本二手车交易系统预期设计的角色主要有管理员、卖家、用户三大模块。系统管理员模块包括:会员信息管理、网站新闻管理、商品信息管理、管理员管理、商品类别管理,新闻类别管理。用户模块包括:安全......
  • (附论文)基于Springboot和Vue的流浪宠物管理系统(179)
    获取源码请滑到最底部访问官网项目配套调试视频和相对应的软件安装包1、项目描述本系统的使用者主要可以被分为管理员角色和用户角色两类。其中,管理员角色主要的功能需求有用户信息管理、流浪宠物管理、流浪动物救助管理等模块,用户角色主要的功能需求有系统登录、查询流浪宠......
  • (附论文)基于Springboot和Vue的社区养老服务平台管理系统(187)
    获取源码请滑到最底部访问官网项目配套调试视频和相对应的软件安装包1、项目描述具体请看视频演示2、项目开发工具开发工具:Idea或Eclipse数据库:MysqlJar包仓库:Maven前端框架:Vue后端框架:Springboot3、项目图片4、演示视频(附论文)基于Springboot和Vue的社区养老......
  • (附论文)基于Springboot和Vue的校园商铺管理系统(188)
    获取源码请滑到最底部访问官网项目配套调试视频和相对应的软件安装包1、项目描述本次开发的校园商铺管理系统实现了收货地址管理、购物车管理、字典管理、公告信息管理、商家管理、商品管理、商品收藏管理、商品评价管理、商品订单管理、用户管理、管理员管理等功能。具体请......
  • (附论文)基于Springboot和Vue的美食烹饪互动平台系统(215)
    获取源码请滑到最底部访问官网项目配套调试视频和相对应的软件安装包1、项目描述美食烹饪互动平台可以实现美食知识类型管理,美食管理,美食留言管理,美食收藏管理,美食知识管理,用户管理等功能。具体请看视频演示2、项目开发工具开发工具:Idea或Eclipse数据库:MysqlJar包仓库:Ma......
  • 计算机毕业设计 小区运动中心预约管理系统 Java+SpringBoot+Vue 前后端分离 文档报告
    ......
  • Spring Cloud项目搭建
    一、SpringCloud简介Springcloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等。它运行环境简单,可以在开发人员电脑上运行。另外说明Springcloud是基于springboot的。二、创建服务......
  • 课程设计-基于Springboot+Vue的校园志愿者管理系统的设计与实现(源码+LW+包运行)
    源码获取:https://download.csdn.net/download/u011832806/89455011基于SpringBoot+Vue的校园志愿者管理系统开发语言:Java数据库:MySQL技术:SpringBoot+MyBatis+Vue.js工具:IDEA/Ecilpse、Navicat、Maven系统演示视频:链接:https://pan.baidu.com/s/1Csd2NBg3TskmgLke2wn8sg?......
  • Spring中的循环依赖:深入解析与解决方案
    Spring中的循环依赖:深入解析与解决方案在Spring框架中,循环依赖(CircularDependency)是一个常见的问题,它发生在两个或多个Bean之间相互依赖,形成一个闭环。循环依赖可能导致应用启动失败、Bean创建失败等问题,影响应用的正常运行。本文将深入探讨Spring中循环依赖的产生原因、......
  • springboot+vue党员之家【程序+论文+开题】-计算机毕业设计
    系统程序文件列表开题报告内容研究背景在新时代背景下,党员队伍的建设与管理面临着前所未有的挑战与机遇。随着信息技术的飞速发展,互联网已成为党员学习、交流和服务的重要平台。传统的党员管理模式逐渐难以满足新时代党员教育管理的需求,亟需一种创新、高效、便捷的信息化管......