Spring Bean的生命周期及三级缓存流程

news2024/12/25 9:14:16

Spring Bean 的生命周期说起来其实就三个大块:实例化Bean -> 设置属性(注入和装配) -> 初始化 -> 使用Bean -> 销毁Bean
这个很好理解,但是内部是怎么样注入,初始化以及销毁?经历怎么样的过程呢?追随这些问题来参考这篇文章。

我们先从 三级缓存 -> 实例化... 顺序逐步理解

一、三级缓存

一级缓存 (singletonObjects) :

        作用:存储完全初始化后的单例Bean对象。

        说明:当一个单例 Bean 被完全初始化之后会被放入到一级缓存中,并且确保其在容器中是唯一的。

二级缓存 (earlySingletonObjects) :

        作用:存储正在创建中的 Bean 对象,即存储正在创建中循环依赖的对象。

        说明:当一个循环依赖的对象在三级缓存中被检测到会进入到二级缓存中进行对象的创建。

三级缓存 (singletonFactories) :

        作用:用于存储 Bean 对象的创建工厂,即 Bean 在其中的工厂进行创建。

        说明:当对象创建好之后会进入 一级缓存。如果检测出存在循环依赖问题则会放入到二级缓存中进行创建。

理解了三级缓存,我们就清楚了初始化及完成后的 Bean 会存放在哪里了。


二、实例化(为 Bean 分配空间)

实例化在初始化之前,是需要给字节码中的这些对象分配空间出来,也就是为 Bean 分配内存空间。


三、设置属性 (Bean 注入和装配)

容器将所需要的属性值(依赖)注入到该 Bean 中的过程,这样 Bean 就可以在后续操作中使用这些属性了。

属性注入和装配的常见三种方式:

1.属性注入 

        使用 @Autowired、@Resource 配合 @Controller、@Service、@Repository、@Configuration、@Compoent 进行注入。

2.setter注入

        在设置 set 方法的上面加上 @Autowired 注解。

例:

@Controller
public class UserController {
    
    // 注入 Setter
    private User user;

    @Autowired
    public void setUser(User user) {
        this.user = user;
    }
}

3.构造器注入

        在构造方法中进行注入

例:

@Controller
public class UserController {

    // 构造方法注入
    private User user;

    @Autowired
    public UserController(User user) {
        this.user = user;
    }
}

当只有一个构造方法的时候可以省略 @Autowired。

经历这个阶段后 Bean 对象的属性会被注入进去。


四、初始化

  • 通过各种通知 Aware 方法,如 BeanNameAware 可以让对象在当前容器中的名称,BeanFactoryAware 可以获取工厂来获取其他对象,ApplicationContextAware 拥有比 BeanNameAware 更多的功能。
  • 执行 BeanPostProcessor 初始化前置方法,执行 postProcessBeforeInitialzation 方法。
  • 执行 PostConstruct 初始化方法,如果 Bean 上标记了 @PostConstruct 注解,那么在依赖注入完成后,会调用被标记的方法。这个注解表示该方法是在 Bean 初始化的最后阶段执行的,用于执行特定的初始化逻辑。
  • 执行自己指定的 init-method 方法,在配置 xml 文件中,这个方法会在依赖注入和 @PostConstruct 方法执行后被调用。
  • 执行 BeanPostProcessor 初始化后置方法,执行 postProcessAfterInitialization 方法。

代码实例:

BeanLifeComponent

//                        BeanLifeComponent 

package com.controller;

import com.Student.Student;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

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

@Component
public class BeanLifeComponent implements BeanNameAware, BeanFactoryAware, BeanPostProcessor {

    private String beanName;
    private BeanFactory beanFactory;

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        System.out.println("执行了BeanNameAware的通知,这个方法可以让Bean知道容器的名称。 此时 bean 容器名称是 " + beanName);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
        System.out.println("执行了 setBeanFactory 获取到容器这样可以手动获取其他的Bean对象");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("执行了@PostConstruct初始化方法。");
    }

    public void init() {
        System.out.println("执行了xml的 init-method 指定初始化方法");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("执行了PreDestory销毁方法");
    }


    public void doSomething() {
        Student student = beanFactory.getBean(Student.class);
        System.out.println("通过 beanFactory 获取到 student,信息为: " + student);
    }

}
Application 类:
//                                  Application
import com.controller.BeanLifeComponent;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Application {
    public static void main(String[] args) {
        // ClassPathXmlApplicationtext 是 ApplicaitonContext的子类,拥有销毁的方法
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        BeanLifeComponent beanLifeComponent = context.getBean("myComponent",BeanLifeComponent.class);

        System.out.println("使用Bean");
        // 销毁 Bean
        beanLifeComponent.doSomething();
        context.destroy();
        System.out.println("销毁了Bean");
    }
}

运行结果如下: 

执行了BeanNameAware的通知,这个方法可以让Bean知道容器的名称。 此时 bean 容器名称是 myComponent
执行了 setBeanFactory 获取到容器这样可以手动获取其他的Bean对象
执行了@PostConstruct初始化方法。
执行了xml的 init-method 指定初始化方法
使用Bean
通过 beanFactory 获取到 student,信息为: Student(name=张三, age=18)
执行了PreDestory销毁方法
销毁了Bean

Process finished with exit code 0

这个实例证明了上述的初始化过程。

接下来这个实例证明一下 @PostConstruct 方法和 @BeanPostProcessor 的顺序:

package com.Test;

import javafx.application.Application;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
class MyComponent {
    public MyComponent() {
        System.out.println("MyComponent constructor");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("MyComponent @PostConstruct");
    }
}

@Component
class MyBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("Before Initialization: " + beanName);
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("After Initialization: " + beanName);
        return bean;
    }
}

public class Main {
    public static void main(String[] args) {
//        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("spring-config.xml");
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        MyComponent myComponent = context.getBean(MyComponent.class);
        System.out.println("Main class: Bean retrieved");
    }
}

运行结果如下:

MyComponent constructor
Before Initialization: myComponent
MyComponent @PostConstruct
After Initialization: myComponent
Main class: Bean retrieved

Process finished with exit code 0

 


五、使用 Bean

正常使用 Bean 实现业务逻辑实现


六、销毁 Bean

销毁 Bean 是指在关闭容器的时候,Spring 会执行一些清理工作,以释放资源或者执行必要的关闭操作。如果不显式定义销毁方法可能不会释放比如数据库连接等资源。他只会尝试在容器中释放单例 Bean 的资源。但是对于原型(prototype)的 Bean ,Spring 不自动执行销毁方法,因为原型 Bean 的生命周期不受 Spring 容器管理。

一、@PreDestory注解

@Component
public class MyBean {
    @PreDestroy
    public void preDestroy() {
        // 执行销毁前的逻辑
    }
}

二、实现 DisposableBean 接口

@Component
public class MyBean implements DisposableBean {
    @Override
    public void destory() throws Exception {
        // 执行销毁方法
    }
}

三、在配置文件中指定 destory-method

在 XML 配置文件中,通过 destroy-method 属性来指定 Bean 的销毁方法。

<bean id="myBean" class="com.example.MyBean" destroy-method="customDestroyMethod" />

这时,需要在 MyBean 类中定义一个 customDestoryMethod 的方法。

这就是完整的 Bean 生命周期

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/994380.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode(力扣)332.重新安排行程Python

LeetCode332.重新安排行程 题目链接代码 题目链接 https://leetcode.cn/problems/reconstruct-itinerary/ 代码 class Solution:def backtracking(self, tickets, used, cur, result, path):if len(path) len(tickets) 1:result.append(path[:])return Truefor i, ticket…

软件设计开发笔记4:QT操作SQLite数据库

有时候我们需要在软件中记录一些历史数据以便于对数据的查询。而我们希望软件不能太复杂&#xff0c;体量也不要太大&#xff0c;这个时候就需要如SQLite这样轻量级的数据库。这篇中我们就来讨论如何在使用QT开发应用是操作SQLite数据库。 0、概述 SQLite是一款开源、轻量级、…

SpringBoot原理-自动配置-原理分析-@Conditional

前言 在自动配置类中声明Bean的时候&#xff0c;除了在方法上添加Bean注解&#xff0c;还会加上Conditionalxxx的注解&#xff08;该注解都是条件装配的注解&#xff09; Conditional 作用&#xff1a;按照一定的条件进行判断&#xff0c;在满足给定条件后才会注册对应的bea…

CSP 202203-1 未初始化警告

答题 要注意是xi和yi的范围&#xff0c;yi可以是0为常数。 #include<iostream> using namespace std;int main() {int n,k;cin>>n>>k;bool*initializenew bool[n]{false};int result0,x,y;while(k--){cin>>x>>y;if(y&&!initialize[y-1…

E. Hanging Hearts

Problem - E - Codeforces 思路&#xff1a;我们考虑用树形dp&#xff0c;用f[i][0]表示以i为根&#xff0c;并且当前节点不在最长上升子序列中&#xff0c;用f[i][1]表示以i为根&#xff0c;当前节点在最长上升子序列中&#xff0c;那么f[i][0]max(f[j][0],f[j][1])&#xff0…

4年经验来面试20K的测试岗,连基础都不会,还不如招应届生!

公司前段时间缺人&#xff0c;也面了不少测试&#xff0c;结果竟然没有一个合适的。一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资在10-20k&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。 看简历很多都是3、4年工作经验&#xf…

使用Python 进行分析

在当今竞争激烈的互联网时代&#xff0c;对于网站的SEO优化至关重要。本文将介绍一种强大的秘密武器&#xff1a;使用Python 进行竞争对手网站分析。通过这种技术&#xff0c;您可以深入了解竞争对手的网站结构、关键词排名和优化策略&#xff0c;为您的SEO优化工作提供有力支持…

网络原理

网络原理 传输层 UDP 特点 特点&#xff1a;无连接&#xff0c;不可靠&#xff0c;面向数据报&#xff0c;全双工 格式 怎么进行校验呢&#xff1f; 把UDP数据报中的源端口&#xff0c;目的端口&#xff0c;UDP报文长度的每个字节&#xff0c;都依次进行累加 把累加结果&a…

跨境电商产业链,服务商的“霸道”你见识过吗?(测评补单)

跨境电商行业的服务商众多&#xff0c;涉及到从前期培训和店铺注册准备到中期选品软件、营销服务、流量投放和支付等多个环节。然而&#xff0c;行业乱象也日益严重&#xff0c;出现了一些不良现象&#xff0c;如恶意竞争、高价要求、割韭菜等。 卖家在选择服务商时应谨慎&…

自适应t分布变异的黏菌优化算法,MATLAB代码

本期为大家带来的是&#xff1a;自适应t分布变异的黏菌优化算法。分别在CEC2005,CEC2017,CEC2021和CEC2022上进行测试&#xff0c;自适应t分布变异的黏菌优化算法(DTSMA)均有非常不错的表现&#xff01;大家可以将此文章中提到的改进策略用于别的智能算法的改进。 参考文献&…

arduino的包含库文件定义配合vsCode查看最初定义

记录这个方式是一个意外发现 一个工程例子说明情况 这个示例工程是一个再oled显示屏上显示的arduino程序。font.h中包含的是字符和图片的取模数组&#xff0c;也就是很多点亮led阵列的数组 下面的就是16*8点阵的字模矩阵&#xff0c;矩阵的值的来历可以参考资料 使用vs cod…

nodejs下载指定版本

1.搜索nodejs打开官网nodejs官网&#xff08;除了去官网下载之外还可以使用nvm下载&#xff09; 2.点击downloads 3.往下滑点击Previous Releases(以前的版本) 4.找到你想下载的版本点开&#xff08;此处可能没你想要的具体版本&#xff0c;没关系找到大版本号相同的点开就行了…

用“居委会”实现差异化竞争,蔚来的品牌社区是怎样创造价值的?|新能源车专题研究...

主笔&#xff1a;浣芳黛 出品&#xff1a;增长黑盒研究组 增长黑盒近期开展的新能源车专题研究&#xff0c;旨在深入挖掘新势力们营销与运营的“真经”&#xff0c;上一期研究了极氪之后&#xff0c;马上有热心读者在后台求写蔚来。毋庸置疑&#xff0c;在造车新势力这条竞争日…

黑马JVM总结(二)

&#xff08;1&#xff09;栈 栈帧对应一次方法的调用&#xff0c;线程是要执行代码的&#xff0c;这些代码都是由一个个方法组成&#xff0c;线程运行的时候每个方法需要的内存叫做一个栈帧 &#xff08;2&#xff09;栈的演示 Frames&#xff1a;相当有栈 方法相当于栈帧…

大数据导论 笔记

一、大数据方向 1、技术发展 计算机网络云计算大数据时代人工智能&#xff08;本科&#xff1a;使用&#xff0c;研究生&#xff1a;推导&#xff0c;博士&#xff1a;创新&#xff09; 2023年 大数据模型 人工智能元年 2、基础课程 hadoop 大数据基础 三大件&#xff1a;HDF…

计算机网络原理 网络层

一&#xff0c;网络层的几个重要概念 1&#xff0c;网络层提供的两种服务 在计算机网络领域&#xff0c;网络层应该向运输层提供怎样的服务&#xff08;“面向连接”还是“无连接”&#xff09;引起了长期的争论。争论的焦点就是&#xff1a;在计算机通信中&#xff0c;可靠交…

Kafka3.0.0版本——消费者(RoundRobin分区分配策略以及再平衡)

目录 一、RoundRobin 分区分配策略原理二、RoundRobin分区分配策略代码案例2.1、创建带有7个分区的sixTopic主题2.3、创建三个消费者 组成 消费者组2.3、创建生产者2.4、测试2.5、RoundRobin分区分配策略代码案例说明 三、RoundRobin 分区分配再平衡案例3.1、停止某一个消费者后…

MySQL--MySQL表的增删改查(进阶)

check 聚合查找 count sum average max min 我们这里先构造出多张表 查询lisi同学的成绩 来自student和来自score c 增加名字这一条件 查询所有同学的总成绩以及个人信息 来自score和来自student 查询所有同学的各科成绩以及个人信息 来自student&#xff0c;course和…

数据分享|SAS数据挖掘EM贷款违约预测分析:逐步Logistic逻辑回归、决策树、随机森林...

全文链接&#xff1a;http://tecdat.cn/?p31745 近几年来&#xff0c;各家商业银行陆续推出多种贷款业务&#xff0c;如何识别贷款违约因素已经成为各家商业银行健康有序发展贷款业务的关键&#xff08;点击文末“阅读原文”获取完整数据&#xff09;。 相关视频 在贷款违约预…

Python开源项目周排行 2023年第33周

#2023年第33周2023年9月9日1feapder款上手简单&#xff0c;功能强大的 Python 爬虫框架&#xff0c;内置 AirSpider、Spider、TaskSpider、BatchSpider 四种爬虫解决不同场景的需求。命名源于 fast-easy-air-pro-spider 缩写。 支持断点续爬、监控报警、浏览器渲染、海量数据去…