SpringBean 的生命周期

本文深入探讨了Spring Bean的生命周期,包括实例化、属性设置、初始化和销毁等阶段。详细阐述了各个阶段接口的实现,如InitializingBean、DisposableBean,以及@PostConstruct、@PreDestroy注解的使用。同时,介绍了Bean的初始化时机,如singleton和prototype作用域的区别,以及如何通过配置指定初始化和销毁方法。此外,还通过示例展示了Bean的创建、使用和销毁过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、整体流程

【Instantiation 实例化阶段】

  1. Spring 读取 xml 配置文件,容器就会调用doCreateBean方法进行实例化。底层是通过工厂+反射完成的创建。

【Populate 设置属性阶段】

  1. 利用依赖注入完成 Bean 中所有属性值的配置注入。

【Initialization 初始化阶段】

  1. 如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 传入当前 Bean 的 id 值。
  2. 如果 Bean 实现了 BeanClassLoaderAware 接口,则执行 setBeanClassLoader()。
  3. 如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 传入当前工厂实例的引用。
  4. 如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 传入当前 ApplicationContext 实例的引用。
  5. 如果实现了 BeanPostProcessor 的前置处理方法,执行postProcessBeforeInitialization()对 bean 进行前置初始化。此处非常重要,Spring 的 AOP 就是利用它实现的。
  6. 如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet()。该方法是在所有bean对象完成实例化后执行的。如果在配置文件中通过 init-method 属性指定了初始化方法,则继续调用该方法。
  7. 如果实现了 BeanPostProcessor 的后置处理方法,执行postProcessAfterInitialization()可以获取该bean对象添加一些默认值的属性,甚至可以返回一个代理对象。此时,Bean 已经可以被应用系统使用了。

初始化方法有三个(顺序调用):

  • @PostConstruct 注解标注的方法
  • InitializingBean 的 afterPropertiesSet()
  • 配置的 init-method

三个方法效果都是一样的,开发中选择其中一种方式就行,一般选择1、3方法中的一个。

【Destruction 销毁阶段】

  1. 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 将 Spring 中的 Bean 销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将继续调用该方法。

销毁方法有三个(顺序调用):

  • @PreDestroy 注解标注的方法
  • DisposableBean 接口的 destroy 方法
  • 配置的 destroy-method

三个方法效果都是一样的,开发中选择其中一种方式就行,一般选择1、3方法中的一个。

注意:
Spring 为 Bean 提供了全面细致的生命周期过程,通过实现特定的接口或 <bean> 的属性设置,都可以对 Bean 的生命周期过程产生影响。虽然可以配置 <bean> 的属性,但是建议不要过多使用 Bean 实现接口,因为会导致代码和 Spring 的聚合过于紧密。

二、说明

了解 SpringBean 生命周期的意义,是可以在其存活期间的指定时刻完成一些相关操作。这种时刻可能有很多,一般情况下,会在 Bean 被初始化后和被销毁前执行一些相关操作。一般 JavaBean 的生命周期很简单,使用关键字 new 实例化 Bean,当不需要该 Bean 时,由 Java 自动进行垃圾回收。而 Spring 根据Bean的作用域来选择管理。对于 singleton 作用域的 Bean,Spring 能够精确地知道该 Bean 何时被创建,何时初始化完成,以及何时被销毁。而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。Spring Bean 的生命周期可以类比人的一生:

实例化:
第 1 步,实例化一个 Bean 对象
出生:作为一个自然人降临在这个世界

属性赋值:
第 2 步,为 Bean 设置相关属性和依赖
登记:登记身份证号,姓名,正式成为人类社会的一份子

初始化:
第 3、4 步为在初始化前执行,5、6步是真正的初始化,第 7 步在初始化后执行,初始化完成之后,Bean就可以被使用了。
成长:接受教育,成为对社会有用的人

使用:
工作:为社会创造价值

销毁:
第 8~10步,第8步其实也可以算到销毁阶段,但不是真正意义上的销毁,而是先在使用前注册了销毁的相关调用接口,为了后面第9、10步真正销毁 Bean 时再执行相应的方法。
死亡:人死如灯灭,不过人这盏灯灭了,还要把灯台埋起来

Bean实例化的时机分为两种,BeanFactory管理的Bean是在使用到的时候才会实例化,ApplicantContext管理的Bean在容器初始化的时候就会完成实例化。BeanFactory就是相对原始一些的社会,ApplicantContext是发达健全的现代社会。

三、理解

在这里插入图片描述

  1. Bean容器在配置文件中找到Person Bean的定义,这个可以说是妈妈怀上了。

  2. Bean容器使用Java 反射API创建Bean的实例,孩子出生了。

  3. Person声明了属性no、name,它们会被设置,相当于注册身份证号和姓名。如果属性本身是Bean,则将对其进行解析和设置。

  4. Person类实现了BeanNameAware接口,通过传递Bean的名称来调用setBeanName(),相当于起个学名。

  5. Person类实现了BeanFactoryAware接口,通过传递BeanFactory对象的实例来调用setBeanFactory(),就像是选了一个学校。

  6. PersonBean实现了BeanPostProcessor接口,在初始化之前调用postProcessBeforeInitialization(),相当于入学报名。

  7. PersonBean类实现了InitializingBean接口,在设置了配置文件中定义的所有Bean属性后,调用afterPropertiesSet(),就像是入学登记。

  8. 配置文件中的Bean定义包含init-method属性,该属性的值将解析为Person类中的方法名称,初始化的时候会调用这个方法,成长不是走个流程,还需要自己不断努力。

  9. Bean Factory对象如果附加了Bean 后置处理器,就会调用postProcessAfterInitialization(),毕业了,总得拿个证。

  10. Person类实现了DisposableBean接口,则当Application不再需要Bean引用时,将调用destroy(),简单说,就是人蹬腿儿了。

  11. 配置文件中的Person Bean定义包含destroy-method属性,所以会调用Person类中的相应方法定义,相当于选好地儿,埋了。

创建一个PersonBean,让它实现几个特殊的接口,来观察一下它的生命周期。

package ioc;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;


public class PersonBean implements InitializingBean, BeanFactoryAware, BeanNameAware, DisposableBean {
    /**
     * 身份证号
     */
    private Integer id;
    /**
     * 姓名
     */
    private String name;

    public PersonBean() {
        System.out.println("1.调用构造方法:我出生了!");
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("2.设置属性:我的名字叫" + name);
    }

    @Override
    public void setBeanName(String s) {
        System.out.println("3.调用BeanNameAware#setBeanName方法:我要上学了,起了个学名");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("4.调用BeanFactoryAware#setBeanFactory方法:选好学校了");
    }

    @PostConstruct
    public void initPostConstruct() {
        System.out.println("6-1.注解@PostConstruct初始化");
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("6-2.InitializingBean#afterPropertiesSet方法:入学登记");
    }

    //通过<bean>的init-method属性指定的初始化方法
    public void initMethod() {
        System.out.println("6-3.init-method:xml配置初始化");
    }

    public void work() {
        System.out.println("8.Bean使用中:work,只有对社会没有用的人才放假。。");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("9-1.注解销毁@PreDestroy");
    }

    @Override
    public void destroy() {
        System.out.println("9-2.DisposableBean#destroy方法:平淡的一生落幕了。");
    }

    //通过<bean>的destroy-method属性指定的销毁方法
    public void destroyMethod() {
        System.out.println("9-3.destroy-method:xml配置销毁");
    }

}

实现了InitializingBean、BeanFactoryAware、BeanNameAware、DisposableBean四个接口。定义了id、name两个属性和对应的getter、setter方法。定义了一个实例方法work。

MyBeanPostProcessor:

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("5.BeanPostProcessor.postProcessBeforeInitialization方法:到学校报名啦");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("8.BeanPostProcessor#postProcessAfterInitialization方法:终于毕业,拿到毕业证啦!");
        return bean;
    }
}

定义配置文件applicationContext.xml。定义init-method和destroy-method。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">

    <bean name="personBean" class="ioc.PersonBean" init-method="initMethod" destroy-method="destroyMethod">
        <property name="id" value="666888"/>
        <property name="name" value="xxp"/>
    </bean>
    
    <!-- 配置注解@PostConstruct、@PreDestroy用 -->
    <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
    <bean name="myBeanPostProcessor" class="ioc.MyBeanPostProcessor"/>
</beans>

测试:

@Test
public void test1() {
    System.out.println("Spring容器初始化===========================");
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    System.out.println("Spring容器初始化完毕========================");
    PersonBean personBean = (PersonBean) context.getBean("personBean");
    personBean.work();
    context.destroy();
    System.out.println("Spring容器关闭==========================");
    context.close();
}

运行结果:

Spring容器初始化===========================
.
.
.
org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
1.调用构造方法:我出生了!
2.设置属性:我的名字叫xxp
3.调用BeanNameAware#setBeanName方法:我要上学了,起了个学名
4.调用BeanFactoryAware#setBeanFactory方法:选好学校了
5.BeanPostProcessor.postProcessBeforeInitialization方法:到学校报名啦
6-1.注解@PostConstruct初始化
6-2.InitializingBean#afterPropertiesSet方法:入学登记
6-3.init-method:xml配置初始化
7.BeanPostProcessor#postProcessAfterInitialization方法:终于毕业,拿到毕业证啦!
Spring容器初始化完毕========================
8.Bean使用中:work,只有对社会没有用的人才放假。。
十月 11, 2022 10:13:28 下午 org.springframework.context.support.AbstractApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@67117f44: startup date [Tue Oct 11 22:13:27 CST 2022]; root of context hierarchy
9-1.注解销毁@PreDestroy
9-2.DisposableBean#destroy方法:平淡的一生落幕了。
9-3.destroy-method:xml配置销毁
Spring容器关闭==========================

Process finished with exit code 0

AbstractApplicationContext类的refresh方法,是AplicationContext容器初始化的关键点。方法调用了finishBeanFactoryInitialization方法,这个方法里调用了getBean方法,getBean方法里调用了AbstractBeanFactory的getBean方法。
在这里插入图片描述

经过一系列调用,到达了Bean创建的方法:doGetBean(),该方法里可以看到Bean的实例化,赋值、初始化的过程。至于最终的销毁可关注下ConfigurableApplicationContext#close()。

四、bean 的初始化时机

bean 对象无外乎是在以下两个时刻进行实例化的:

  1. Spring 容器启动时。
  2. 调用 getBean() 时。

bean 对象在何时进行实例化,这与 Bean 的作用域有关系。测试如下:

public class PersonServiceBean implements PersonService {    
  public PersonServiceBean() {  
    System.out.println("我被实例化了");   
  }     
  @Override    
  public void save() {     
    System.out.println("我是save()"); 
  }
}

1️⃣当 Spring 的配置文件——beans.xml 的内容为:

 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">

<bean id="personService"
	class="cn.service.impl.PersonServiceBean"></bean> 

</beans>

即 bean 的作用域为 singleton 时,修改 SpringTest 类的代码为:

public class SpringTest {     
   @Test    
    public void test() {        
       // 实例化Spring容器   
       ApplicationContext ctx = 
       	new ClassPathXmlApplicationContext("beans.xml");
    } 
}

test() 输出:我被实例化了

这说明当 bean 的作用域为 singleton 时,bean 对象是在 Spring 容器启动时就进行创建了。即默认情况下会在容器启动时初始化 bean,但也可以指定 bean 节点的lazy-init=“true”来延迟初始化 bean。此时,只有初次获取 bean 时才初始化 bean。修改 Spring 配置文件 beans.xml:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"
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">     

<bean id="personService"
	class="cn.service.impl.PersonServiceBean" 
		lazy-init="true"></bean>

</beans>

lazy-init=”true”指定了不要在 Spring 容器启动时对该 bean 进行实例化。此时,执行 test(),就不会输出我被实例化了。这时,修改 SpringTest:

public class SpringTest {     
    @Test   
    public void test() {        
     //实例化Spring容器
     ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
     //从Spring容器取得bean
     PersonService personService = (PersonService) ctx.getBean("personService");
    } 
}

再次执行 test(),输出:我被实例化了

若对所有 bean 都应用延迟初始化,可以在根节点 beans 设置default-lazy-init=“true”,如下:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"
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" 
default-lazy-init="true">     
...... 
</beans>

2️⃣当 Spring 的配置文件——beans.xml 的内容为:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"
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">   
  
<bean id="personService"
	class="cn.service.impl.PersonServiceBean" 
			scope="prototype"></bean>
</beans>

即 bean 的作用域为 prototype 时,若 SpringTest 类的代码为:

public class SpringTest {     
  @Test    
  public void test() {        
    // 实例化Spring容器 
    ApplicationContext ctx = 
    	new ClassPathXmlApplicationContext("beans.xml"); 
  } 
}

执行 test(),没有输出这句话:我被实例化了。这就说明当 bean 的作用域为 prototype 时,bean 对象并不会在 Spring 容器启动时就进行创建。修改 SpringTest:

public class SpringTest {     
    @Test    
     public void test() {        
       // 实例化Spring容器
       ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); 
       // 从Spring容器取得bean
       PersonService personService = (PersonService) ctx.getBean("personService"); 
  }
}

再次执行 test(),输出:我被实例化了。证实了当 bean 的作用域为 prototype 时,bean 对象将会在调用 getBean() 时进行创建。

五、指定bean的初始化方法和销毁方法

为了达到在 bean 被初始化的时候,就初始化某些资源的目的,可修改 PersonServiceBean 类的代码为:

public class PersonServiceBean implements PersonService {
    public void init() {
        System.out.println("初始化某些资源");
    }
    public PersonServiceBean() {
        System.out.println("我被实例化了");
    }
    @Override
    public void save() {
        System.out.println("我是save()");
    }
}

这样,目的就具体地成为:当 Spring 容器初始化 PersonServiceBean 对象之后,就要执行该对象的 init()。修改 Spring 配置文件 beans.xml:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"
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">    
 
<bean id="personService"
	class="cn.service.impl.PersonServiceBean" 
lazy-init="false" init-method="init" />

</beans>

若 SpringTest 类的代码为:

public class SpringTest {
    @Test
    public void test() {
        // 实例化Spring容器     
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
    }
}

执行 test(),输出: 输出

现在又希望在 bean 被销毁的时候,就释放或关闭某些资源。修改 PersonServiceBean:

public class PersonServiceBean implements PersonService {
    public void init() {
        System.out.println("初始化某些资源");
    }
    public PersonServiceBean() {
        System.out.println("我被实例化了");
    }
    @Override
    public void save() {
        System.out.println("我是save()");
    }
    public void destroy() {
        System.out.println("释放初始化的资源");
    }
}

bean 对象到底是什么时候销毁的呢?答案是:如果没有人为地删除它,默认该 bean 一直在 Spring 容器中,也就是说随着 Spring 容器的关闭,该 bean 才会被销毁。修改 Spring 配置文件 beans.xml:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"
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">     

<bean id="personService" class="cn.service.impl.PersonServiceBean" 
lazy-init="false" init-method="init" destroy-method="destroy" /> 

</beans>

最后,修改 SpringTest:

public class SpringTest {
    @Test
    public void test() {
        // ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); 
        // 实例化Spring容器         
        AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        // 正常关闭Spring容器  
        ctx.close();
    }
}

执行 test(),输出:输出
这就是 Spring 管理的 Bean 的生命周期。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JFS_Study

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值