Spring Bean的生命周期解读

目录

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();


    }
}

测试截图

至此这篇文章到此结束。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据库信息速递 DataStax与谷歌合作将向NoSQL AstraDB引入向量搜索技术

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

2023-06-23:redis中什么是缓存击穿?该如何解决?

2023-06-23&#xff1a;redis中什么是缓存击穿&#xff1f;该如何解决&#xff1f; 答案2023-06-23&#xff1a; 缓存击穿是指一个缓存中的热点数据非常频繁地被大量并发请求访问&#xff0c;当该热点数据失效的瞬间&#xff0c;持续的大并发请求无法通过缓存获取到数据&…

数学建模-数据的处理

MATLAB数学建模方法与实践&#xff08;第3版&#xff09;——读书笔记 数据的准备数据获取数据处理缺失值处理噪音过滤数据集成数据归约数据变换标准化离散化 数据统计基本描述性统计分布描述性统计 数据可视化数据降维主成分分析&#xff08;PCA&#xff09;相关系数降维 数据…

【数据结构】排序

插入排序 把当前遍历到的元素前的元素序列是排好序的,把当前元素放到前边的序列中进行排序。 直接插入排序 不带哨兵 void InsertSort(int A[],int n) { int i,j,temp; for(i1;i<n;i) if(A[i]<A[i-1]) { tempA[i]; for(ji-1;j>0 && A[j]>temp;--j) A[j…

网络安全系统教程+学习路线(自学笔记)

一、什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面…

34岁上岸,我终于圆了自己的考研梦

​ 大家好&#xff0c;我是独孤风&#xff0c;一位曾经的港口煤炭工人&#xff0c;目前在某国企任大数据负责人&#xff0c;公众号大数据流动的作者。 ​ 虽然告诉自己要平静&#xff0c;但是当接到EMS录取通知书的那一刻&#xff0c;眼眶还是忍不住有些湿润。今年正好是是东北…

青岛大学_王卓老师【数据结构与算法】Week03_04_线性表的链式表示和实现4_学习笔记

本文是个人笔记&#xff0c;仅用于学习分享&#xff0c;素材来自青岛大学王卓老师的教学视频&#xff0c;如有侵权&#xff0c;请留言作删文处理。 视频链接&#xff1a; 数据结构与算法基础–第3周04–2.5线性表的链式表示和实现4–单链表基本操作2–销毁单链表 &#x1f4…

Linux——进程的概念

task_struct task_struct 是linux下管理进程的结构&#xff0c;称为PCB&#xff0c;进程控制块。linux所有的指令本质上都是一个进程。进程 task_struct 进程的数据、代码、可执行程序&#xff0c;有属性、有内容。 进程是系统的工作单元。系统由多个进程组成&#xff0c;包…

ChatGPT在物流与运输行业的智能场景:智能调度和自动驾驶的前瞻应用

第一章&#xff1a;引言 随着人工智能技术的飞速发展&#xff0c;物流与运输行业正迎来一场革命。传统的调度和运输模式已经无法满足快速增长的物流需求和客户期望。在这一领域&#xff0c;ChatGPT作为一种先进的自然语言处理模型&#xff0c;具有巨大的潜力。本文将探讨ChatG…

第三十五章Java面向对象概念及封装、继承、多态三种特性详解

面向对象简称 OO&#xff08;Object Oriented&#xff09;&#xff0c;20 世纪 80 年代以后&#xff0c;有了面向对象分析&#xff08;OOA&#xff09;、 面向对象设计&#xff08;OOD&#xff09;、面向对象程序设计&#xff08;OOP&#xff09;等新的系统开发方式模型的研究。…

ECC加密算法详解+python实现

一.前言 目前比较受欢迎的加密算法一共存在两种&#xff0c;一种是基于大整数因子分解问题&#xff08;IFP&#xff09;的RSA算法和基于椭圆曲线上离散对数计算问题&#xff08;ECDLP&#xff09;的ECC算法。之前对RSA算法进行过很详细的讲解&#xff0c;但是ECC加密算法还没有…

数据库的操作

前言 在之前的文章中&#xff0c;我们已经了解了什么是数据库&#xff0c;以及为什么有数据库&#xff0c;和数据库有什么作用&#xff0c;有了这些宏观概念之后&#xff0c;本章为大家进一步详细介绍对于数据库在Linux上如何具体操作。 1.创建数据库 1.1创建数据库语法 语法…

第十二章 EfficientNetv2网络详解

系列文章目录 第一章 AlexNet网络详解 第二章 VGG网络详解 第三章 GoogLeNet网络详解 第四章 ResNet网络详解 第五章 ResNeXt网络详解 第六章 MobileNetv1网络详解 第七章 MobileNetv2网络详解 第八章 MobileNetv3网络详解 第九章 ShuffleNetv1网络详解 第十章…

【线程池】史上最全的ThreadPoolExecutor源码详解

目录 一、线程池框架 1.1 第一层结构 1.2 接口简介 1.3 核心实现类 1.4 辅助类 1.5 完成服务 二、ThreadPoolExecutor的成员属性和内部类 2.1 主要成员属性以及工具方法 2.2 五种内部类 2.2.1 拒绝策略内部类&#xff08;Policy&#xff09; 2.2.2 工作线程内部类&a…

HHU云计算期末复习(下)Hadoop、虚拟化技术、openstack

文章目录 第五章 Hadoop分布式文件系统HDFS分离元数据和数据&#xff1a;NameNode和DataNode流水线复制 第七章 虚拟化技术7.1 虚拟化技术简介7.2 虚拟机迁移7.3 网络虚拟化 第八章 openstack8.1 计算服务NovaRabbitMQ 8.2 Swift 第九章 云计算数据中心9.1 云数据中心特征9.2 网…

【MySQL数据库】MySQL 高级SQL 语句一

[TOC](MySQL 高级SQL 语句 一、MySQL 高级SQL 语句1.1select -显示表格中一个或数个字段的所有数据记录1.2distinct不显示重复的数据记录1.3where有条件查询1.4and、or且 或1.5in 显示已知的值的数据记录1.6between 显示两个值范围内的数据记录1.7通配符&#xff0c;通常通配符…

一文了解云计算

目录 &#x1f34e;云服务 &#x1f34e;云计算类型 &#x1f352;公有云 &#x1f352;私有云 &#x1f352;混合云 &#x1f34e;云计算服务模式 &#x1f352;IaaS基础设施即服务 &#x1f352;PaaS平台即服务 &#x1f352;SaaS软件即服务 &#x1f352;三者之间区别 &…

回波数据adc_data.bin解析(附MATLAB程序)

毫米波雷达系统性能参数分析 1、xWR1642—DCA1000 TI目前有两款采集卡TSW1400和DCA1000&#xff0c;可以为xWR1243/1443和1642毫米波雷达进行回波数据采集。本文将主要介绍几款雷达分别用2款采集卡数据采集的回波数据格式以及MATLAB数据解析程序。 1、xWR1642—DCA1000 &…

打造专属个人模型-私有独立离线模型部署-阿里云GPU服务器配置

阿里云有免费的机器学习 GPU 服务器&#xff0c;免费试用活动页https://free.aliyun.com只要没有申请过 PAI-DSW 资源的新老用户皆可申请 5000CU 的免费额度&#xff0c;3个月内使用。 选择第一个进行立即试用 可以看到试用的界面 如果遇到下面的错误&#xff0c;当前账号没有权…

蓝桥杯专题-试题版-【数字游戏】【城市建设】【最大子阵】【蚂蚁感冒】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…