• Spring之bean的生命周期


    目录

    一、bean生命周期的原理

    二、Spring的单例&&多例模式

            2.1 理论知识

            2.2 代码论证


    一、bean生命周期的原理

    1.1 了解bean的生命周期

    首先我们就要思考一个问题,那就是:

    Spring是管理项目中所有的javabean对象;

    这些bean对象又是什么时候生?什么时候提供服务?什么时候销毁?

    我们可以一起来看看这张bean的生命周期图: 运行逻辑是从下往上根据箭头来看的;

     

    我们一起来分析一下: 

    第一步:

    通过XML、Java annotation(注解)以及Java Configuration(配置类)

    等方式加载Spring Bean;

    第二步:

    :解析Bean的定义。

    在Spring容器启动过程中,会将Bean解析成Spring内部的BeanDefinition结构;

    理解为:将spring.xml中的标签转换成BeanDefinition结构

    有点类似于XML解析;

    第三步: 

    BeanDefinition:包含了很多属性和方法。

    例如:id、class(类名)、scope、ref(依赖的bean)等等。

    其实就是:

    将bean(例如)的定义信息存储到这个对应BeanDefinition相应的属性中;

    例如:

    -----> BeanDefinition(id/class/scope)

     第四步: 

    BeanFactoryPostProcessor:是Spring容器功能的扩展接口。

    注释:

    1)BeanFactoryPostProcessor在spring容器加载完BeanDefinition之后,

          在bean实例化之前执行的

    2)对bean元数据(BeanDefinition)进行加工处理,

         也就是BeanDefinition属性填充、修改等操作

    结论1:

    当我们运行这行代码时:

    ClassPathXmlApplicationContext context =

    new ClassPathXmlApplicationContext("/spring-context.xml");

    会对Spring的配置文件进行建模,然后把bean标签转成BeanDefinition这个类,

    这个时候BeanDefinition里面还没有值。

    所以它会在被实例化之前被进行加工处理。

      第五步: 

    BeanFactory:bean工厂。它按照我们的要求生产我们需要的各种各样的bean。

    解释案例:

    1. 例:
    2. BeanFactory -> List<BeanDefinition>
    3. BeanDefinition(id/class/scope/init-method)
    4. <bean class="com.zking.spring02.biz.BookBizImpl"/>
    5. foreach(BeanDefinition bean : List<BeanDefinition>){
    6. //根据class属性反射机制实例化对象
    7. //反射赋值设置属性
    8. }

     第六步: 

    Aware感知接口:在实际开发中,经常需要用到Spring容器本身的功能资源;

    具体应用案例:

    BeanNameAware、ApplicationContextAware等等

    BeanDefinition 实现了 BeanNameAware、ApplicationContextAware

    结论2:

    就是Aware感知接口帮助我们自己写的 bean对象 去获取到 spring容器 内部 内置对象。

     第七步: 

    BeanPostProcessor:后置处理器。

    在Bean对象实例化和引入注入完毕后,

    再显示调用初始化方法的前后添加自定义的逻辑。(类似于AOP的绕环通知)

    然后就完成Bean的创建; 

     第八步: 

    destory:销毁。

     简单点做个总结整个生命周期的流程:

    ①:通过三种方式(配置文件、注解、配置类)将bean标签转成BeanDefinition对象;

    ③:通过BeanFactoryPostProcessor类,可以在初始化之前修改属性值;

    ④ :BeanFactory类进行bean实例化,也就是生产javabean;

    ⑤:Aware感知接口,能够在拿到Spring上下文中内部的资源对象;

    ⑥:BeanPostProcessor后置处理器,相当于环绕通知。

    最后就是创建Bean成功,destory进行销毁。


    二、Spring的单例&&多例模式

            2.1 理论知识

            然而Spring的javaBean是分为两种的,一种是单例,一种是多例;

            我们在Spring配置文件中也是可以配置的: 

                    scope="singleton"   ---> 代表单例

                    scope="prototype"  ---> 代表多例

            不配置的话默认就是单例;

    1. <bean class="impl.UserBizImpl" id="userBiz" scope="singleton">bean>
    2. <bean class="impl.UserBizImpl" id="userBiz" scope="prototype">bean>

                    2.1.1 两种模式有什么区别?

                            我们先来看看一段代码:

    1. public static void main(String[] args) {
    2. //实例化四个对象
    3. Person p1 = new Person();
    4. Person p2 = new Person();
    5. Person p3 = new Person();
    6. Person p4 = new Person();
    7. //依次打印
    8. System.out.println(p1);
    9. System.out.println(p2);
    10. System.out.println(p3);
    11. System.out.println(p4);
    12. }

                    打印结果:

                     

                            我们由此思考一下: 

                            一个类如果被使用了一百次会创建多少个对象?

                    如果这个项目一共有一万个类呢?        答:100w个对象

                            结论:创建了很多不必要的对象会极大的占用空间。

                    所以我们就需要用到了单例模式:

    1. package com.leaf.beanLife;
    2. public class Demo1 {
    3. public static void main(String[] args) {
    4. //实例化
    5. // Person p1 = new Person();
    6. // Person p2 = new Person();
    7. // Person p3 = new Person();
    8. // Person p4 = new Person();
    9. Person p1 = Person.newInstance();
    10. Person p2 = Person.newInstance();
    11. Person p3 = Person.newInstance();
    12. Person p4 = Person.newInstance();
    13. System.out.println(p1);
    14. System.out.println(p2);
    15. System.out.println(p3);
    16. System.out.println(p4);
    17. }
    18. }
    19. /**
    20. * 实体类:人类
    21. * @author Leaf
    22. *
    23. * 2022年8月9日 上午3:41:38
    24. */
    25. class Person{
    26. //私有化构造方法,让其他地方不能创建;
    27. private Person() {
    28. }
    29. //封装私有化的这个类
    30. private final static Person p = new Person();
    31. //封装一个方法,返回私有化的该类;
    32. public static Person newInstance() {
    33. return p;
    34. }
    35. }

    通过我们私有化构造方法,修改部分代码用到了单例模式后,我们再次运行:

    这样我们就算把一个类在一百个地方使用,使用的都是同一个对象;

    再次思考:

    整个项目一共有1w个类,Spring上下文需要创建多少个对象?

    答:1w个对象

    到这里我们也就明显知道单例模式与多例的区别了,

    单例模式的优点:就是极大的节省了空间,省略了很多的没必要的对象创建。

    所以Spring默认使用单例模式;

     但是,Spring之所以还提供了多例模式供我们选择,

    原因呢就是:单例模式一样存在弊端!

    例如:变量污染的问题;

    生活中的案例:给孩子买玩具:

    A:买2份玩具,两个孩子都开心,不会有矛盾。

    B:买1份玩具,两个孩子可能会有争执;

    轮流玩,孩子"小明"先玩了一上午后,孩子"小白"再去玩感觉就不一样了,

    觉得污染了,染上了泥巴什么的,就会有争吵。

    两种买玩具的方式有利有弊,

    买两份呢:钱包有压力。买一份呢:兄弟容易打架。

    结论:

    A就相当于多例模式,B就相当于单例模式。

    所以Spring就视情况而定,提供两种模式供我们按情况来选择。


            2.2 代码论证

            我们经过生活中的案例,已经把关于单例模式与多例模式的理论方面的理解 全部分析讲出来了,接着 我们就要用代码 实操 印证一下我们的结论了。

            首先我们准备一些测试的准备类:

            InstanceFactory:验证初始化javabean

    1. package com.leaf.beanLife;
    2. /**
    3. * 为了印证 BeanPostProcessor 初始化javabean对象
    4. * @author Leaf
    5. *
    6. * 2022年8月9日 上午5:06:39
    7. */
    8. public class InstanceFactory {
    9. public void init() {
    10. System.out.println("初始化方法");
    11. }
    12. public void destroy() {
    13. System.out.println("销毁方法");
    14. }
    15. public void service() {
    16. System.out.println("业务方法");
    17. }
    18. }

      ParamAction:验证单例模式与多例模式的区别

    1. package com.leaf.beanLife;
    2. import java.util.List;
    3. import com.leaf.ioc.biz.UserBiz;
    4. import impl.UserBizImpl1;
    5. import impl.UserBizImpl2;
    6. /**
    7. * 印证单例模式与多例模式的区别
    8. * @author Leaf
    9. *
    10. * 2022年8月9日 上午5:07:27
    11. */
    12. public class ParamAction {
    13. private int age;
    14. private String name;
    15. private List hobby;
    16. private int num = 1;
    17. // private UserBiz userBiz = new UserBizImpl1();
    18. public ParamAction() {
    19. super();
    20. }
    21. public ParamAction(int age, String name, List hobby) {
    22. super();
    23. this.age = age;
    24. this.name = name;
    25. this.hobby = hobby;
    26. }
    27. public void execute() {
    28. // userBiz.upload();
    29. // userBiz = new UserBizImpl2();
    30. System.out.println("this.num=" + this.num++);
    31. System.out.println(this.name);
    32. System.out.println(this.age);
    33. System.out.println(this.hobby);
    34. }
    35. }

      Demo2:测试类

    1. package com.leaf.beanLife;
    2. import org.junit.Test;
    3. import org.springframework.beans.factory.BeanFactory;
    4. import org.springframework.beans.factory.xml.XmlBeanFactory;
    5. import org.springframework.context.ApplicationContext;
    6. import org.springframework.context.support.ClassPathXmlApplicationContext;
    7. import org.springframework.core.io.ClassPathResource;
    8. import org.springframework.core.io.Resource;
    9. /*
    10. * 测试类
    11. * spring bean的生命週期
    12. * spring bean的單例多例
    13. */
    14. public class Demo2 {
    15. // 体现单例与多例的区别
    16. @Test
    17. public void test1() {
    18. ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
    19. // ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
    20. ParamAction p1 = (ParamAction) applicationContext.getBean("paramAction");
    21. ParamAction p2 = (ParamAction) applicationContext.getBean("paramAction");
    22. // System.out.println(p1==p2);
    23. p1.execute();
    24. p2.execute();
    25. // 单例时,容器销毁instanceFactory对象也销毁;多例时,容器销毁对象不一定销毁;
    26. applicationContext.close();
    27. }
    28. // 体现单例与多例的初始化的时间点 instanceFactory
    29. @Test
    30. public void test2() {
    31. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
    32. }
    33. // BeanFactory会初始化bean对象,但会根据不同的实现子类采取不同的初始化方式
    34. // 默认情况下bean的初始化,单例模式立马会执行,但是此时XmlBeanFactory作为子类,单例模式下容器创建,bean依赖没有初始化,只有要获取使用bean对象才进行初始化
    35. @Test
    36. public void test3() {
    37. // ClassPathXmlApplicationContext applicationContext = new
    38. // ClassPathXmlApplicationContext("/spring-context.xml");
    39. Resource resource = new ClassPathResource("/spring-context.xml");
    40. BeanFactory beanFactory = new XmlBeanFactory(resource);
    41. // InstanceFactory i1 = (InstanceFactory) beanFactory.getBean("instanceFactory");
    42. }
    43. }

      spring-context.xml:Spring配置文件

    1. <bean id="paramAction" class="com.leaf.beanLife.ParamAction">
    2. <constructor-arg name="name" value="三丰">constructor-arg>
    3. <constructor-arg name="age" value="21">constructor-arg>
    4. <constructor-arg name="hobby">
    5. <list>
    6. <value>抽烟value>
    7. <value>烫头value>
    8. <value>大保健value>
    9. list>
    10. constructor-arg>
    11. bean>
    12. <bean id="instanceFactory" class="com.leaf.beanLife.InstanceFactory"
    13. scope="prototype" init-method="init" destroy-method="destroy">
    14. bean>

      然后我们依次测试:

            ①:体现单例与多例的区别:

                    下面这个就是相当于单例:

             然后我们改变一下配置:

    然后再来运行测试: 

    我们还要知道一个结论:

            单例模式:容器生对象生,容器死对象死。

            多例模式:使用时对象生,死亡跟着jvm垃圾回收机制走。

    以上这些就是单例和多模式的区别啦。 

    并且bean的初始化时间点,除了跟bean管理模式(单例&多例)有关之外;

    还跟BeanFactory的子类有关。


    OK,以上就是Leaf今天带来的关于Spring管理的javabean的生命周期以及他的两种模式的知识分享啦,我们下期再见啦!!!

  • 相关阅读:
    美国访问学者博士后面签常见问题
    【Java 进阶篇】JDBC工具类详解
    小白刷力扣 之SQL学习计划 第3天(第1667题,第1484题,第1527题)
    C#——字符串
    [附源码]JAVA毕业设计基于vue技术的汽车维修检测系统设计与实现(系统+LW)
    Python关于 *args 和 **kwargs 参数的详解(全)
    如何给玩偶建模并让它跳个舞?
    Oracle-动态sql学习笔记,由易至难讲解七个例子
    嵌入式linux系统内核启动过程分享
    JRS303-数据校验
  • 原文地址:https://blog.csdn.net/qq_63492318/article/details/126239273