Spring之bean的生命周期

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Spring之bean的生命周期

Spring Bean的生命周期:

一、通过XML、Java annotation(注解)以及Java Configuration(配置类),等方式加载Spring Bean

二、BeanDefinitionReader:解析Bean的定义。在Spring容器启动过程中,会将Bean解析成Spring内部的BeanDefinition结构;理解为:将spring.xml中的<bean>标签转换成BeanDefinition结构(有点类似于XML解析)

三、BeanDefinition:包含了很多属性和方法。例如:id、class(类名)、scope、ref(依赖的bean)等等。其实就是将bean(例如<bean>)的定义信息

存储到这个对应BeanDefinition相应的属性中

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

注意:

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

在bean实例化之前执行的

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

属性填充、修改等操作

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

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

例如:BeanNameAware、ApplicationContextAware等等

BeanDefinition 实现了 BeanNameAware、ApplicationContextAware

七、BeanPostProcessor:后置处理器。在Bean对象实例化和引入注入完毕后,

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

前提条件:如果检测到Bean对象实现了BeanPostProcessor后置处理器才会执行

Before和After方法

BeanPostProcessor

1)Before

2)调用初始化Bean(InitializingBean和init-method,Bean的初始化才算完成)

3)After

完成了Bean的创建工作

八、destory:销毁

1.Bean的初始化过程

1.xml/annotation/configuation/配置JavaBean

2.BeanDefinitionReader解析配置的JavaBean得到Beandefinition,最终得到List<BeanDefinition>集合

3.触发BeanFactoryPostProcessor,在JavaBean初始化之前执行

4.Spring中的BeanFactory,会通过List<BeanDefinition>集合遍历初始化所有的JavaBean对象

5.如果自己的JavaBean需要调动Spring上下文中的资源(方法或属性),那么需要实现*Aware感知接口

6.如果自己的JavaBean已经初始化好了,还需扩展功能,那么需要借助BeanPostProcessor后置处理器来实现

具体图解与流程图如下:

1.1代码详解

BeanFactoryPostProcessor是Spring容器功能的扩展接口,怎么理解?比如假设我们现在有一个内部类Person,其原本有三个属性。BeanFactoryPostProcessor的作用就是可以在初始化对象之前可以做一个补充,它内部类原本只有三个属性,我们可以给它加一个关联属性User进去,并对新增的关联属性User对象做出相对应的处理,具体代码详解如下:

package com.kissship.beanlife;
/**
 * @author Kissship
 * @site www.Kissship.com
 * @company xxx公司
 * @create 2023-08-18-14:17
 */
public class Demo1 {
    public static void main(String[] args) {
//     BeanDefinition
    }
}
class User{
//无值
}
class Person{
    private int pid;
    private String name;
    private String sex;
    private User user;//扩展新增的关联属性(无值)
    public int getPid() {
        return pid;
    }
    public void setPid(int pid) {
        this.pid = pid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Person() {
    }
    //在初始化对象之前可以做一个补充,比如上面原本只有三个属性,我们也可以给它加一个关联属性User
    public Person(int pid, String name, String sex) {
        this.init();//扩展的内容
        this.pid = pid;
        this.name = name;
        this.sex = sex;
    }
    //这里可以针对我们新增的关联属性User对象作出处理
    public void init() {
    }
}

解析配置得到List集合与遍历初始化Bean对象,具体代码详解如下:

import org.springframework.beans.factory.config.BeanDefinition;
import java.util.ArrayList;
import java.util.List;
/**
 * @author Kissship
 * @site www.Kissship.com
 * @company xxx公司
 * @create 2023-08-18-14:17
 */
public class Demo1 {
    //初始化Bean
    public static void main(String[] args) throws ClassNotFoundException {
//            2.BeanDefinitionReader解析配置的JavaBean得到Beandefinition,最终得到List<BeanDefinition>集合
        List<BeanDefinition> beans = new ArrayList<>();//BeanDefinitionReader把spring-context.xml中所有的bean标签都解析成了List集合
        for (BeanDefinition bean : beans) {
//            4.Spring中的BeanFactory,会通过List<BeanDefinition>集合遍历初始化所有的JavaBean对象
          String beanClassName = bean.getBeanClassName();//得到的就是spring-context.xml中bean标签中class里的值
          Class.forName(beanClassName);//将配置的所有JavaBean进行反射实例化
        }
    }
}

如果自己的JavaBean需要调动Spring上下文中的资源(方法或属性),那么需要实现*Aware感知接口(比如BeanDefinition实现了BeanNameAware、ApplicationContextAware接口)具体详解如下:

Aware接口:

实现Aware接口:

1.2思考

那么现在有个问题,当我们的Spring容器初始化Bean完成之后,我们可能还需要加工一下,即进行再初始化。因为并不是每一个类都是我们自己写的。也许我们需要用别人的类内容,然后在此基础上再进行拓展。那么在此时我们能改动别人写的类里面的源码吗?不能。因为这样有可能影响到其他的功能,这时候我们的解决办法就是自己写一个类去继承别人的类然后进行再一波初始化,我们这里以BaseDao为例:

BaseDao为父类,MyBaseDao为子类:

我们需要在继承BaseDao方法后,在子类中进行初始化方法的重写(即再一次初始化),具体如下:

package com.kissship.beanlife;
/**
 * @author Kissship
 * @site www.Kissship.com
 * @company xxx公司
 * @create 2023-08-18-15:58
 */
//继承原本的BaseDao
public class MyBaseDao extends BaseDao{
    public MyBaseDao(){
        //重写初始化方法
        super();
        System.out.println("初始化...");
    }
}

那么在此时如果我们在初始化之后不满意,在初始化之后想修改Bean的内容,可以利用BeanPostProcessor后置处理器来完成。这时候可以在Bean对象实例化和引入注入完毕后,在显示调用初始化方法的前后添加自定义逻辑。(类似于AOP的环绕通知)环绕通知不了解的可以通过下面链接进行跳转至对应博客界面,如下:

那么到这里我们就完成了Bean的创建工作。

2.Bean的单例与多例选择

1.在Spring中JavaBean默认是单例的,但是可以配置多例。

2.单例的优点:节约内存。弊端:有变量污染。

  多例的优点:无变量污染。弊端:极其消耗内存。

3.单例:JavaBean是跟着spring上下文初始化的;容器生对象生,容器死对象死(此处对象指JavaBean)。

  多例:JavaBean在使用时才会创建,销毁跟着jvm走。

4.第三点并不绝对正确,个别情况会有所个例。

2.1论证单例与多例优缺点

准备工作:

ParamAction:

package com.kissship.beanlife;
import java.util.List;
public class ParamAction {
  private int age;
  private String name;
  private List<String> hobby;
  private int num = 1;
  // private UserBiz userBiz = new UserBizImpl1();
  public ParamAction() {
    super();
  }
  public ParamAction(int age, String name, List<String> hobby) {
    super();
    this.age = age;
    this.name = name;
    this.hobby = hobby;
  }
  public void execute() {
    // userBiz.upload();
    // userBiz = new UserBizImpl2();
    System.out.println("this.num=" + this.num++);
    System.out.println(this.name);
    System.out.println(this.age);
    System.out.println(this.hobby);
  }
}

InstanceFactory(Bean工厂):

package com.kissship.beanlife;
public class InstanceFactory {
  public void init() {
    System.out.println("初始化方法");
  }
  public void destroy() {
    System.out.println("销毁方法");
  }
  public void service() {
    System.out.println("业务方法");
  }
}

spring-context.xml进行配置(生命周期部分):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       default-autowire="byType"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--    ioc的javabean-->
<!--    凡是在Spring配置文件spring-context.xml中配置,那么该类javabean就交给了Spring容器管理-->
    <bean class="com.kissship.ioc.web.UserAction" id="userAction">
        <property name="userService" ref="userService"></property>
<!--        <constructor-arg name="uname" value="扎克" ></constructor-arg>-->
<!--        <constructor-arg name="age" value="18" ></constructor-arg>-->
<!--        <constructor-arg name="hobby">-->
<!--        <list>-->
<!--            <value>唱,跳</value>-->
<!--            <value>Rap</value>-->
<!--            <value>篮球</value>-->
<!--        </list>-->
<!--        </constructor-arg>-->
    </bean>
    <bean class="com.kissship.ioc.web.GoodsAction" id="goodsAction">
        <property name="userService" ref="userServiceImpl1"></property>
<!--        <property name="gname" value="小文"></property>-->
<!--        <property name="age" value="19"></property>-->
<!--        <property name="peoples">-->
<!--            <list>-->
<!--                <value>印度飞饼</value>-->
<!--                <value>意大利炮</value>-->
<!--                <value>北京烤鸭</value>-->
<!--                <value>墨西哥卷</value>-->
<!--            </list>-->
<!--        </property>-->
    </bean>
    <bean class="com.kissship.ioc.service.impl.UserServiceImpl2" id="userService"></bean>
    <bean class="com.kissship.ioc.service.impl.UserServiceImpl1" id="userServiceImpl1"></bean>
<!--   aop相关的Javabean-->
    <!--目标对象-->
    <bean class="com.kissship.aop.biz.impl.BookBizImpl" id="bookBiz"></bean>
    <!--通知-->
    <bean class="com.kissship.aop.advice.MyMethodBeforeAdvice" id="methodBeforeAdvice"></bean>
    <bean class="com.kissship.aop.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean>
    <bean class="com.kissship.aop.advice.MyMethodInterceptor" id="myMethodInterceptor"></bean>
    <bean class="com.kissship.aop.advice.MyThrowsAdvice" id="myThrowsAdvice"></bean>
    <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" id="regexpMethodPointcutAdvisor">
        <property name="advice" ref="myAfterReturningAdvice"></property>
        <property name="pattern" value=".*buy"></property>
    </bean>
    <!--代理-->
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy">
<!--        配置目标对象-->
        <property name="target" ref="bookBiz"></property>
<!--        配置代理接口,目标对象的接口-->
        <property name="proxyInterfaces">
            <list>
                <value>com.kissship.aop.biz.IBookBiz</value>
            </list>
        </property>
<!--        配置通知-->
        <property name="interceptorNames">
            <list>
                <value>methodBeforeAdvice</value>
<!--                <value>myAfterReturningAdvice</value>-->
                <value>regexpMethodPointcutAdvisor</value>
                <value>myMethodInterceptor</value>
                <value>myThrowsAdvice</value>
            </list>
        </property>
    </bean>
<!--    Spring的生命周期-->
    <bean class="com.kissship.beanlife.ParamAction" id="paramAction">
        <constructor-arg name="name" value="刘三金"></constructor-arg>
        <constructor-arg name="age" value="19"></constructor-arg>
        <constructor-arg name="hobby">
            <list>
                <value>来自星星的哥</value>
                <value>天空一声巨响</value>
                <value>劳资闪亮登场</value>
            </list>
        </constructor-arg>
    </bean>
    <bean class="com.kissship.beanlife.InstanceFactory" id="instanceFactory"
          scope="prototype" init-method="init" destroy-method="destroy"></bean>
</beans>

测试类Demo2:

package com.kissship.beanlife;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
/*
 * spring bean的生命週期
 * spring bean的單例多例
 */
public class Demo2 {
  // 体现单例与多例的区别
  @Test
  public void test1() {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
//    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
    ParamAction p1 = (ParamAction) applicationContext.getBean("paramAction");
    ParamAction p2 = (ParamAction) applicationContext.getBean("paramAction");
    // System.out.println(p1==p2);
    p1.execute();
    p2.execute();
//    单例时,容器销毁instanceFactory对象也销毁;多例时,容器销毁对象不一定销毁;
    applicationContext.close();
  }
  // 体现单例与多例的初始化的时间点 instanceFactory
  @Test
  public void test2() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
  }
  // BeanFactory会初始化bean对象,但会根据不同的实现子类采取不同的初始化方式
  // 默认情况下bean的初始化,单例模式立马会执行,但是此时XmlBeanFactory作为子类,单例模式下容器创建,bean依赖没有初始化,只有要获取使用bean对象才进行初始化
  @Test
  public void test3() {
    // ClassPathXmlApplicationContext applicationContext = new
    // ClassPathXmlApplicationContext("/spring-context.xml");
    Resource resource = new ClassPathResource("/spring-context.xml");
    BeanFactory beanFactory = new XmlBeanFactory(resource);
//    InstanceFactory i1 = (InstanceFactory) beanFactory.getBean("instanceFactory");
  }
}

执行Demo2结果如下:

那么我们换上多例后会是什么结果?

具体实操,在spring-context.xml中ParamAtion的Bean配置的class属性里加上scope属性并赋值多例(原型模式) ,如下:

修改后我们继续执行Demo2测试类看看效果:

2.2论证初始化时间点

Demo2里已经写了体现单例与多例的初始化时间点的方法,所以我们只需要在spring-context.xml中给它定一个多例的属性值然后测试看效果即可,xml代码如下:

<bean class="com.kissship.beanlife.InstanceFactory" id="instanceFactory"
          scope="prototype" init-method="init" destroy-method="destroy"></bean>

然后我们运行Demo2看看效果,如下:

紧接着我们换成单例,如下:

<bean class="com.kissship.beanlife.InstanceFactory" id="instanceFactory"
          scope="singleton" init-method="init" destroy-method="destroy"></bean>

执行方法看效果,如下:

那么现在问题又来了,虽然验证了单例与多例的初始化时间点,但是看不出到底是不是使用Spring创建的,那么现在我们上代码验证看看结果。刚刚我们在演示时多例并没有执行初始化方法,现在我们把xml文件里的单例改成多例,然后再Demo2测试类中加上以下代码:

然后再运行一次看效果:

2.3个例演示

BeanFactory会初始化bean对象,但会根据不同的实现子类采取不同的初始化方式。

默认情况下bean的初始化,单例模式会立马执行,但是此时XmlBeanFactory作为子类,单例模式下容器创建,bean依赖没有初始化·,只有要获取使用bean对象才进行初始化。

验证:

1.先把xml多例模式改为单例,然后运行Test2方法,再运行Test3方法,比较结果。

2.然后把Test3注释的代码部分放开再执行比较结果。

运行Test2的结果(先注释论证是否为Sprring创建新增的代码再运行):

运行Test3的结果:

然后取消注释,如下:

再执行一次Test3,效果如下:


最后Spring之bean的生命周期就到这里,祝大家在敲代码的路上一路通畅!

感谢大家的观看 !


目录
相关文章
|
2月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
22天前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
21天前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
119 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
9天前
|
XML Java 数据格式
spring复习02,xml配置管理bean
详细讲解了Spring框架中基于XML配置文件管理bean的各种方式,包括获取bean、依赖注入、特殊值处理、属性赋值、集合类型处理、p命名空间、bean作用域及生命周期和自动装配。
spring复习02,xml配置管理bean
|
9天前
|
XML Java 数据格式
spring复习03,注解配置管理bean
Spring框架中使用注解配置管理bean的方法,包括常用注解的标识组件、扫描组件、基于注解的自动装配以及使用注解后的注意事项,并提供了一个基于注解自动装配的完整示例。
spring复习03,注解配置管理bean
|
2月前
|
XML Java 数据格式
Spring5入门到实战------3、IOC容器-Bean管理XML方式(一)
这篇文章详细介绍了Spring框架中IOC容器的Bean管理,特别是基于XML配置方式的实现。文章涵盖了Bean的定义、属性注入、使用set方法和构造函数注入,以及如何注入不同类型的属性,包括null值、特殊字符和外部bean。此外,还探讨了内部bean的概念及其与外部bean的比较,并提供了相应的示例代码和测试结果。
Spring5入门到实战------3、IOC容器-Bean管理XML方式(一)
|
2月前
|
XML Java 数据格式
Spring5入门到实战------5、IOC容器-Bean管理(三)
这篇文章深入探讨了Spring5框架中IOC容器的高级Bean管理,包括FactoryBean的使用、Bean作用域的设置、Bean生命周期的详细解释以及Bean后置处理器的实现和应用。
Spring5入门到实战------5、IOC容器-Bean管理(三)
|
2月前
|
XML Java 数据格式
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
这篇文章是Spring5框架的实战教程,主题是IOC容器中Bean的集合属性注入,通过XML配置方式。文章详细讲解了如何在Spring中注入数组、List、Map和Set类型的集合属性,并提供了相应的XML配置示例和Java类定义。此外,还介绍了如何在集合中注入对象类型值,以及如何使用Spring的util命名空间来实现集合的复用。最后,通过测试代码和结果展示了注入效果。
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
|
2月前
|
XML Java 数据格式
Spring5入门到实战------6、IOC容器-Bean管理XML方式(自动装配)
这篇文章是Spring5框架的入门教程,详细讲解了IOC容器中Bean的自动装配机制,包括手动装配、`byName`和`byType`两种自动装配方式,并通过XML配置文件和Java代码示例展示了如何在Spring中实现自动装配。
Spring5入门到实战------6、IOC容器-Bean管理XML方式(自动装配)
|
2月前
|
XML Java 数据格式
Spring5入门到实战------8、IOC容器-Bean管理注解方式
这篇文章详细介绍了Spring5框架中使用注解进行Bean管理的方法,包括创建Bean的注解、自动装配和属性注入的注解,以及如何用配置类替代XML配置文件实现完全注解开发。
Spring5入门到实战------8、IOC容器-Bean管理注解方式
下一篇
无影云桌面