Spring Bean的生命周期解读

news2025/3/1 3:13:51

目录

1. Spring IOC容器

1.1 Spring IOC 容器的设计

1.1.1 BeanFactory

1.1.2 ApplicationContext

1.2 Spring Bean的生命周期

1.2.1 BeanDefinition

1.2.2 InstantiationAwareBeanPostProcessor和BeanPostProcessor

1.2.3 测试生命周期


1. Spring IOC容器

1.1 Spring IOC 容器的设计

Spring IOC 容器的设计主要是基于BeanFactoryApplicationContext两个接口,其中ApplicationContext是BeanFactory的子接口之一,换句话说BeanFactory是Spring IOC容器所定义的最顶层接口,而ApplicationContext是其高级接口之一,并且对于BeanFactory功能做了许多有用的扩展,所以在绝大部分的工作场景中,都会使用ApplicationContext作为Spring IOC 容器,如下图所示:

 首先我们定义一个User实体类:

public class User {
    String username;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                '}';
    }
}

1.1.1 BeanFactory

BeanFactory的使用注册Bean对象以及获取Bean对象代码如下:

DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        RootBeanDefinition rootBeanDefinition=new RootBeanDefinition(User.class);
        beanFactory.registerBeanDefinition("user",rootBeanDefinition);
        System.out.println(beanFactory.getBean("user",User.class));

1.1.2 ApplicationContext

ApplicationContext的使用注册Bean对象以及获取Bean对象代码如下:

 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(User.class);
        applicationContext.refresh();
        System.out.println(applicationContext.getBean("user", User.class));

1.2 Spring Bean的生命周期

Spring 容器可以管理 singleton 作用域 Bean 的生命周期,在此作用域下,Spring 能够精确地知道该 Bean 何时被创建,何时初始化完成,以及何时被销毁。

而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。每次客户端请求 prototype 作用域的 Bean 时,Spring 容器都会创建一个新的实例,并且不会管那些被配置成 prototype 作用域的 Bean 的生命周期。

生命周期主要是为了了解Spring IOC容器初始化和销毁Bean的过程,通过对它的学习就可以知道如何在初始化和销毁的时候加入自定义的方法,以满足特定的需求。如下图:

从上图可以看到,Spring IoC容器对Bean 的管理还是比较复杂的,Spring loC容器在执行了初始化和依赖注入后,会执行一定的步骤来完成初始化,通过这些步骤我们就能自定义初始化,而在Spring IoC 容器正常关闭的时候,它也会执行一定的步骤来关闭容器,释放资源。除需要了解整个生命周期的步骤外,还要知道这些生命周期的接口是针对什么而言的,首先介绍生命周期的步骤。

1. 如果 Bean 实现了接口 BeanNameAware的setBeanName方法,那么它就会调用这个方法。
2. 如果 Bean 实现了接口 BeanFactoryAware 的 setBeanFactory方法,那么它就会调用这个方法。
3. 如果 Bean实现了接口 ApplicationContextAware 的 setApplicationContext方法,且
Spring loC容器也必须是一个ApplicationContext 接口的实现类,那么才会调用这个方法,否则是不调用的。
4. 如果 Bean 实现了接口 BeanPostProcessor 的 postProcessBeforeInitialization方法,那么它就会调用这个方法。

5 .如果 Bean实现了接口BeanFactoryPostProcessor的afterPropertiesSet方法,那么它就会调用这个方法。
6. 如果 Bean自定义了初始化方法,它就会调用已定义的初始化方法。
7. 如果Bean 实现了接口 BeanPostProcessor 的postProcessAfterInitialization方法,完成了这些调用,这个时候Bean 就完成了初始化,那么 Bean就生存在Spring loC的容器中了,使用者就可以从中获取 Bean的服务。
8. 当服务器正常关闭,或者遇到其他关闭 Spring loC 容器的事件,它就会调用对应的方完成Bean 的销毁,其步骤如下:
        如果Bean实现了接口 DisposableBean 的 destroy方法,那么就会调用它。
        如果定义了自定义的销毁方法,那么就会调用它。

1.2.1 BeanDefinition

Spring容器启动的过程中,会将Bean解析成Spring内部的BeanDefinition结构。不管是是通过xml配置文件的<Bean>标签,还是通过注解配置的@Bean,还是@Compontent标注的类,还是扫描得到的类,它最终都会被解析成一个BeanDefinition对象,最后我们的Bean工厂就会根据这份Bean的定义信息,对bean进行实例化、初始化等等操作。

你可以把BeanDefinition丢给Bean工厂,然后Bean工厂就会根据这个信息帮你生产一个Bean实例,拿去使用。

BeanDefinition里面里面包含了bean定义的各种信息,如:bean对应的class、scope、lazy信息、dependOn信息、autowireCandidate(是否是候选对象)、primary(是否是主要的候选者)等信息。

 RootBeanDefinition类:表示根bean定义信息,通常bean中没有父bean的就使用这种表示。

ChildBeanDefinition类:表示子bean定义信息,如果需要指定父bean的,可以使用ChildBeanDefinition来定义子bean的配置信息,里面有parentName属性,用来指定父bean的名称。

GenericBeanDefinition类:通用的bean定义信息,既可以表示没有父bean的bean配置信息,也可以表示有父bean的子bean配置信息,这个类里面也有parentName属性,用来指定父bean的名称。

ConfigurationClassBeanDefinition类:表示通过配置类中@Bean方法定义bean信息

可以通过配置类中使用@Bean来标注一些方法,通过这些方法来定义bean,这些方法配置的bean信息最后会转换为ConfigurationClassBeanDefinition类型的对象。

AnnotatedBeanDefinition接口:表示通过注解的方式定义的bean信息。

BeanDefinitionBuilder:构建BeanDefinition的工具类

1.2.2 InstantiationAwareBeanPostProcessor和BeanPostProcessor

InstantiationAwareBeanPostProcessor实际上继承了BeanPostProcessor接口。InstantiationAwareBeanPostProcessor作用于实例化阶段的前后,BeanPostProcessor作用于初始化阶段的前后。如下图:

 BeanPostProcessor是一个接口,还有很多子接口,这些接口中提供了很多方法,spring在bean生命周期的不同阶段,会调用BeanPostProcessor中的一些方法,来对生命周期进行扩展,bean生命周期中的所有扩展点都是依靠这个集合中的BeanPostProcessor来实现的。该接口提供了两个函数:postProcessBeforeInitialzation( Object bean, String beanName ) 当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。 这个函数会先于InitialzationBean执行,因此称为前置处理。 所有Aware接口的注入就是在这一步完成的。postProcessAfterInitialzation( Object bean, String beanName ) 当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。 这个函数会在InitialzationBean完成后执行,因此称为后置处理。


Spring Aware是Spring定义的回调接口。何为回调?就是客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数。

1.2.3 测试生命周期

我们自定义一个User实体类,要求Spring容器使用我们自定义的@MyAutowired注解标注的构造方法进行构造Bean对象,然后我们观察在Bean周期的日志打印,更好的理解Bean周期过程。

自定义@MyAutowired注解

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.CONSTRUCTOR)
public @interface MyAutowired {
}

定义User实体类

public class User implements InitializingBean, DisposableBean {
    String username;
    //Spring为了降低对客户代码的侵入性,给bean的配置提供了init-method属性,
    // 该属性指定了在这一阶段需要执行的函数名。Spring便会在初始化阶段执行我们设置的函数。
    // init-method本质上仍然使用了InitializingBean接口。
    public void init(){
        System.out.println(this.getClass().getSimpleName()+" 执行自定义初始化方法");
    }

    public User() {
    }

    @MyAutowired
    public User(String username) {
        this.username = username;
    }


    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                '}';
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("调用DisposableBean接口的destroy方法");
    }
    //afterPropertiesSet()这一阶段也可以在bean正式构造完成前增加我们自定义的逻辑,
    // 但它与前置处理不同,由于该函数并不会把当前bean对象传进来,因此在这一步没办法处理对象本身,
    // 只能增加一些额外的逻辑。 若要使用它,我们需要让bean实现该接口,把要增加的逻辑写在该函数中。
    // 然后Spring会在前置处理完成后检测当前bean是否实现了该接口,并执行afterPropertiesSet函数
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("调用afterPropertiesSet方法");
    }
}

测试代码

public class MyTest {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //InstantiationAwareBeanPostProcessor接口在Bean对象实例化前的方法
        beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {
            public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
                if(beanName.equals("user"))
                System.out.println(beanName+" Bean对象在实例化之前操作**");
                return null;
            }
        });
        //InstantiationAwareBeanPostProcessor接口在Bean对象实例化后的方法
        beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {
            public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
                if(beanName.equals("user"))
                System.out.println(beanName+" Bean对象在实例化之后操作**");
                return false;
            }
        });
        //使用自己自定义含有@MyAutowired注解的构造方法,实例化Bean对象
        beanFactory.addBeanPostProcessor(new SmartInstantiationAwareBeanPostProcessor() {
            public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
                if(beanName.equals("user"))
                System.out.println("实例化Bean对象");
                Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
                List<Constructor<?>> collect = Arrays.stream(declaredConstructors).filter(i -> i.isAnnotationPresent(MyAutowired.class)).collect(Collectors.toList());
                Constructor[] constructors = collect.toArray(new Constructor[collect.size()]);
                return constructors.length>0?constructors:null;

            }
        });
        //BeanPostProcessor接口在Bean对象初始化之前的方法调用
        beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
            @Override
            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                if(beanName.equals("user"))
                    System.out.println(beanName+" Bean对象在初始化之前操作**");
                return null;
            }
        });
       //BeanPostProcessor接口在Bean对象初始化之后的方法调用
        beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if(beanName.equals("user"))
                    System.out.println(beanName+" Bean对象在初始化之后操作**");
                return null;
            }
        });

        RootBeanDefinition rootBeanDefinition= (RootBeanDefinition) BeanDefinitionBuilder.rootBeanDefinition(User.class).setInitMethodName("init").getBeanDefinition();
        beanFactory.registerBeanDefinition("user",rootBeanDefinition);
        beanFactory.registerBeanDefinition("username", BeanDefinitionBuilder.genericBeanDefinition(String.class)
                .addConstructorArgValue("admin").getBeanDefinition());
        System.out.println(beanFactory.getBean("user",User.class));
        beanFactory.destroySingletons();


    }
}

测试截图

至此这篇文章到此结束。

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

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

相关文章

【前端工程化】深入浅出vite(一)--vite的优点及原理、性能优化

Vite 需要 Node.js 版本 14.18&#xff0c;16。然而&#xff0c;有些模板需要依赖更高的 Node 版本才能正常运行&#xff0c;当你的包管理器发出警告时&#xff0c;请注意升级你的 Node 版本。 背景 webpack支持多种模块化&#xff0c;将不同模块的依赖关系构建成依赖图来进行…

帮助中心对企业有用吗?

帮助中心一般是指产品的说明或者使用帮助&#xff0c;客户在利用一些网站的功能或者服务时往往会遇到一些看似很简单&#xff0c;但不经过说明可能很难搞清楚的问题&#xff0c;企业有时甚至会因为这些细节问题的影响而失去用户&#xff0c;其实在很多情况下&#xff0c;只要经…

如何添加mathtype到往word菜单?

mathtype6.9安装完事&#xff0c;总是不在word中显示菜单&#xff0c;网上搜了好几个教程&#xff0c;步骤各不相同&#xff0c;索性自己试了一遍&#xff0c;整理了下关键步骤&#xff0c;做个备份&#xff0c;下次直接看自己的经验&#xff01; 把math type安装目录中&#…

MySQL中常用工具

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xf…

LeetCode 15.三数之和

文章目录 题目描述解题思路代码 题目描述 链接&#xff1a;https://leetcode.cn/problems/3sum 解题思路 排序 双指针 注意点&#xff1a;去重 代码 public IList<IList<int>> ThreeSum(int[] nums) {// 结果数组List<IList<int>> result new Li…

Nacos 2.x版本 配置中心和服务注册与发现 源码解析

一、配置中心源码解析 1.首先找到nacos读取配置的入口 1&#xff09;找到nacos-config包下的spring.factories中的NacosConfigBootstrapConfiguration 2&#xff09;NacosConfigBootstrapConfiguration会做两件事情&#xff0c;加载完成两个bean&#xff0c;一个是NacosConfi…

2023品牌新媒体矩阵营销洞察报告:流量内卷下,如何寻找增长新引擎?

近年来&#xff0c;随着移动互联网的发展渗透&#xff0c;短视频、直播的兴起&#xff0c;新消费/新零售、兴趣电商/社交电商等的驱动下&#xff0c;布局线上渠道已成为绝大多数品牌的必然选择。 2022年&#xff0c;越来越多的品牌加入到自运营、自播的行列中&#xff0c;并且…

【线性代数】快速复习笔记

线性代数快速复习 行列式行列式的基础计算某行&#xff08;列加上或减去另一行&#xff08;列的几倍&#xff0c;行列式不变某行列乘k,等于k乘此行列式互换两行列&#xff0c;行列式变号 行列式的性质1 主对角线是X&#xff0c;其余是其他常数a2 范德蒙德行列式3 行列式加减法4…

Linux中安装配置启动Redis

Linux中安装配置启动Redis 一、下载redis 使用命令下载&#xff1a; wget https://download.redis.io/releases/redis-6.0.10.tar.gzls du sh redis-6.0.10.tar.gz解压 [rootnode02 ~]# tar xzf redis-6.0.10.tar.gz [rootnode02 ~]# cd redis-6.0.10 [rootnode02 redis-6.…

低代码:改变未来的智慧力量!打造智能产业新纪元!

前言 在数智时代的浪潮中&#xff0c;智慧产业成为推动经济发展的重要引擎。随着科技的不断进步&#xff0c;传统工厂也在加速转型为智能工厂&#xff0c;实现产业体系的智能化建设已经成为当今企业追求的目标。 概念 产业体系智能化是指借助信息技术手段&#xff0c;对传统产业…

redis高可用(二)

redis高可用&#xff08;二&#xff09; 一、主从复制 1.概念 主从复制&#xff0c;是指将一台Redis服务器的数据&#xff0c;复制到其他的Redis服务器。前者称为主节点(Master)&#xff0c;后者称为从节点(Slave)&#xff1b;数据的复制是单向的&#xff0c;只能由主节点到…

Dlib —— 对图片进行人脸检测并绘出特征(附C++源码)

效果 注意&#xff1a;Dlib检测人脸在Release版耗时与CPU有关,本人I7 10代约100ms左右。建议人脸检测可以考虑使用Yolov5进行&#xff0c;之后将检测到的人脸输入给Dlib做特征或其他。 代码 #include <iostream>#include <dlib/image_processing/frontal_face_detecto…

【胖虎的逆向之路】——GOT/PLT Hook详解针对自定义so库的Hook实操

文章目录 [TOC](文章目录) 前言一、ELF 是什么&#xff1f;1、ELF 的概念2、ELF 的组成2.1、 两种视图是什么呢&#xff1f;2.2、 ELF文件头又是什么西西&#xff1f;2.2、 ELF中节头表是什么南南呢&#xff1f;2.2、 ELF中程序头表是什么北北呢&#xff1f; 二、动态库装载、动…

uni-app 微信小程序发布时,主包超过2M限制

小程序发布时&#xff0c;提示超过2M&#xff0c;无法通过&#xff0c;此时可以尝试以下几种方法&#xff1a; 1、对图片做压缩 图片尽量放在服务器端&#xff0c;使用的时候&#xff0c;通过URL路径获取&#xff0c;若不得已放在本地时&#xff0c;可以对图片进行压缩&#…

基于springboot+Redis的前后端分离项目之分布式锁-redission(五)-【黑马点评】

&#x1f381;&#x1f381;资源文件分享 链接&#xff1a;https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA?pwdeh11 提取码&#xff1a;eh11 分布式锁-redission 分布式锁-redission1 分布式锁-redission功能介绍2 分布式锁-Redission快速入门3 分布式锁-redission可重入锁…

NoSQL之Redis优化(一)

Redis的高可用 一、Redis 持久化RDB 持久化AOF 持久化RDB和AOF的优缺点 二、Redis 性能管理内存碎片如何产生的&#xff1f;解决碎片率大的问题&#xff1a;内存使用率内回收key 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时…

【Java可执行命令】(四)反编译工具javap:深入解析应用程序反编译工具javap ~

Java可执行命令详解之javap 1️⃣ 概念2️⃣ 优势和缺点3️⃣ 使用3.1 语法格式3.1.1 可选参数&#xff1a;-l3.1.2 可选参数&#xff1a;-c3.1.3 可选参数&#xff1a;-s3.1.4 可选参数&#xff1a;-verbose3.1.5 可选参数&#xff1a;-version 4️⃣ 应用场景5️⃣ 注意事项&…

6.21、设计模式 单例设计模式

1 设计模式&#xff08;Design pattern&#xff09; 代表了最佳的实践&#xff0c;通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来…

好用又智能笔记类工具有哪些?

在工作和生活中&#xff0c;我们经常面临大量信息和任务需要记录和整理。好用又智能的笔记类工具成为了办公人士提高工作效率和组织信息的必备利器。 敬业签笔记工具支持分类记录笔记&#xff0c;可以根据不同的主题或项目进行整理。无论是工作笔记、学习笔记还是个人生活记录…

OpenFeign——请求其他服务时传递token信息

文章目录 前言准备流程初测定义nacos-product子服务定义服务的消费方 cloudalibaba-openfeign-server初步测试结论 设置cloudalibaba-openfeign-server中的feign总结 前言 在实际开发过程中&#xff0c;服务与服务之间都会有比较频繁的通信操作。其次不同用户所需要查询的数据…