Spring bean生命周期

文章目录

  • 一、Bean实例化
    • [1.1 加载Spring Bean](#1.1 加载Spring Bean)
    • [1.2. 解析Bean的定义](#1.2. 解析Bean的定义)
    • [1.3. Bean属性定义](#1.3. Bean属性定义)
    • [1.4. BeanFactoryPostProcessor 扩展接口](#1.4. BeanFactoryPostProcessor 扩展接口)
    • [1.5. 实例化Bean对象](#1.5. 实例化Bean对象)
    • [1.6. Aware感知](#1.6. Aware感知)
    • [1.7. 初始化方法](#1.7. 初始化方法)
    • [1.8. 后置处理](#1.8. 后置处理)
    • [1.9. destroy 销毁](#1.9. destroy 销毁)
  • 二、Bean的单例与多例模式
    • [2.1 单例模式](#2.1 单例模式)
    • [2.2 多例模式](#2.2 多例模式)
    • [2.3 实例](#2.3 实例)
    • [2.4 总结](#2.4 总结)

一、Bean实例化

1.1 加载Spring Bean

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

Bean:这是指在Spring容器启动时,通过配置文件、注解或配置类等方式告诉Spring框架需要创建哪些Bean。

1.2. 解析Bean的定义

BeanDefinitionReader:这是一个解析器,它将Bean的定义解析成Spring内部的BeanDefinition结构。可以将其理解为将配置文件中的标签转换为Spring框架内部使用的数据结构。

1.3. Bean属性定义

BeanDefinition:它包含了Bean的各种属性和方法,例如id(标识符)、class(类名)、scope(作用域)、ref(依赖的Bean)等。BeanDefinition实际上是将配置文件中的标签的定义信息存储到对应的BeanDefinition属性中。

1.4. BeanFactoryPostProcessor 扩展接口

BeanFactoryPostProcessor:这是Spring容器的扩展接口,它在Spring容器加载完BeanDefinition之后、Bean实例化之前执行。它可以对Bean的元数据(BeanDefinition)进行加工处理,例如填充或修改BeanDefinition的属性。

1.5. 实例化Bean对象

BeanFactory:它是Bean的工厂,根据我们的要求生产各种不同的Bean。BeanFactory根据配置文件中的BeanDefinition信息来实例化Bean对象,并进行属性赋值。(根据class属性反射机制实例化对象,反射赋值设置属性)

1.6. Aware感知

Aware 感知是一种机制,它允许在Bean的生命周期中获取对Spring容器的访问权限。通过实现相应的Aware接口,Bean可以获得与Spring容器的交互能力,以便在特定的生命周期阶段执行自定义操作。

在Spring框架中,有多个Aware接口可供实现,每个接口都提供了不同的功能。以下是一些常见的Aware接口及其作用:

  • BeanNameAware:通过实现BeanNameAware接口,Bean可以获取自己在Spring容器中的名称。这对于需要知道自己在容器中的标识的Bean非常有用。

  • ApplicationContextAware:通过实现ApplicationContextAware接口,Bean可以获取对Spring应用上下文的引用。这使得Bean能够在需要时访问Spring容器中的其他Bean或执行其他与容器相关的操作。

  • BeanFactoryAware:通过实现BeanFactoryAware接口,Bean可以获取对Spring Bean工厂的引用。这使得Bean能够在需要时访问Bean工厂中的其他Bean或执行其他与Bean工厂相关的操作。

    通过实现这些Aware接口,Bean可以在初始化过程中获得对Spring容器的访问权限,并在需要时执行特定的操作。这为开发人员提供了更大的灵活性和控制权,使得他们能够根据自己的需求自定义Bean的行为。

1.7. 初始化方法

如果Bean定义中指定了初始化方法,容器会在属性赋值完成后调用该方法进行一些初始化操作。可以使用@PostConstruct注解或者实现InitializingBean接口来指定初始化方法。如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。

1.8. 后置处理

BeanPostProcessor:它是一个后置处理器,在Bean对象实例化和依赖注入完成后,在显示调用初始化方法之前或之后添加自定义的逻辑。可以将其类比为AOP中的环绕通知。

注:只有当检测到Bean对象实现了BeanPostProcessor接口时,才会执行Before和After方法。

BeanPostProcessor的执行顺序是:

Before方法 -> 初始化Bean(InitializingBean接口和init-method方法)-> After方法。

1.9. destroy 销毁

destroy:这是Bean的销毁阶段,如果这个Bean的Spring配置中配置了destroy-method属性,在Spring容器关闭时调用。在销毁阶段,可以执行一些清理工作,例如释放资源等。

二、Bean的单例与多例模式

2.1 单例模式

单例模式是指在整个应用程序中只创建一个Bean实例。

当使用单例模式时,Spring容器在首次请求该Bean时创建一个实例,并将其缓存起来。之后的每次请求都会返回同一个实例。

  • 优点:减少了对象创建和销毁的开销,节省了系统资源。
  • 缺点:由于共享同一个实例,可能导致状态信息被多个线程共享,需要注意线程安全问题。

2.2 多例模式

多例模式是指每次请求时都会创建一个新的Bean实例。

当使用多例模式时,Spring容器在每次请求该Bean时都会创建一个新的实例,而不是返回之前创建的实例。

  • 优点:每次请求都会返回一个全新的实例,避免了状态信息共享的问题。
  • 缺点:频繁创建和销毁对象可能导致系统资源消耗增加。

2.3 实例

假设我们有一个简单的Number类,用于计数:

haskell 复制代码
public class Number {
    private int count;
 
    public int getCount() {
        return count;
    }
 
    public void increment() {
        count++;
    }
}

我们将使用Spring配置来演示单例和多例模式的区别。

2.3.1 单例模式:

haskell 复制代码
<bean id="counter" class="com.xqx.Number" scope="singleton" />
public class SingletonDemo {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Counter count1 = context.getBean("number", Number.class);
        Counter count2 = context.getBean("number", Number.class);
 
        counter1.increment();
        System.out.println("Count 1: " + count1.getCount()); // 输出:Count 1: 1
        System.out.println("Count 2: " + count2.getCount()); // 输出:Count 2: 1
    }
}

在单例模式下,count1和count2引用的是同一个实例,因此它们的计数值是相同的。

2.3.2 多例模式:

haskell 复制代码
<bean id="counter" class="com.xqx.Number" scope="prototype" />
public class PrototypeDemo {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
         Counter count1 = context.getBean("number", Number.class);
        Counter count2 = context.getBean("number", Number.class);
 
        counter1.increment();
        System.out.println("count 1: " + count1.getCount()); // 输出:count 1: 1
        System.out.println("count 2: " + count2.getCount()); // 输出:Count 2: 0
    }
}

在多例模式下,count1和count2引用的是不同的实例,因此它们的计数值是独立的。

2.4 总结

单例模式和多例模式各有优缺点,具体使用哪种模式取决于应用程序的需求和设计考虑。在需要共享状态或资源的场景下,可以使用单例模式;在需要独立状态或避免状态共享的场景下,可以使用多例模式。

相关推荐
m0_5719575825 分钟前
Java | Leetcode Java题解之第543题二叉树的直径
java·leetcode·题解
魔道不误砍柴功2 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_2343 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨3 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
测开小菜鸟4 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity5 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天5 小时前
java的threadlocal为何内存泄漏
java
caridle5 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^5 小时前
数据库连接池的创建
java·开发语言·数据库
苹果醋35 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx